##// END OF EJS Templates
merge
marcink -
r1419:febb3f95 merge beta
parent child Browse files
Show More
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
@@ -1,395 +1,399 b''
1 """
1 """
2 Routes configuration
2 Routes configuration
3
3
4 The more specific and detailed routes should be defined first so they
4 The more specific and detailed routes should be defined first so they
5 may take precedent over the more generic routes. For more information
5 may take precedent over the more generic routes. For more information
6 refer to the routes manual at http://routes.groovie.org/docs/
6 refer to the routes manual at http://routes.groovie.org/docs/
7 """
7 """
8 from __future__ import with_statement
8 from __future__ import with_statement
9 from routes import Mapper
9 from routes import Mapper
10 from rhodecode.lib.utils import check_repo_fast as cr
10 from rhodecode.lib.utils import check_repo_fast as cr
11
11
12 # prefix for non repository related links needs to be prefixed with `/`
12 # prefix for non repository related links needs to be prefixed with `/`
13 ADMIN_PREFIX = '/_admin'
13 ADMIN_PREFIX = '/_admin'
14
14
15
15
16 def make_map(config):
16 def make_map(config):
17 """Create, configure and return the routes Mapper"""
17 """Create, configure and return the routes Mapper"""
18 rmap = Mapper(directory=config['pylons.paths']['controllers'],
18 rmap = Mapper(directory=config['pylons.paths']['controllers'],
19 always_scan=config['debug'])
19 always_scan=config['debug'])
20 rmap.minimization = False
20 rmap.minimization = False
21 rmap.explicit = False
21 rmap.explicit = False
22
22
23 def check_repo(environ, match_dict):
23 def check_repo(environ, match_dict):
24 """
24 """
25 check for valid repository for proper 404 handling
25 check for valid repository for proper 404 handling
26 :param environ:
26 :param environ:
27 :param match_dict:
27 :param match_dict:
28 """
28 """
29 repo_name = match_dict.get('repo_name')
29 repo_name = match_dict.get('repo_name')
30 return not cr(repo_name, config['base_path'])
30 return not cr(repo_name, config['base_path'])
31
31
32
32
33 def check_int(environ, match_dict):
33 def check_int(environ, match_dict):
34 return match_dict.get('id').isdigit()
34 return match_dict.get('id').isdigit()
35
35
36
36
37
37
38
38
39 # The ErrorController route (handles 404/500 error pages); it should
39 # The ErrorController route (handles 404/500 error pages); it should
40 # likely stay at the top, ensuring it can always be resolved
40 # likely stay at the top, ensuring it can always be resolved
41 rmap.connect('/error/{action}', controller='error')
41 rmap.connect('/error/{action}', controller='error')
42 rmap.connect('/error/{action}/{id}', controller='error')
42 rmap.connect('/error/{action}/{id}', controller='error')
43
43
44 #==========================================================================
44 #==========================================================================
45 # CUSTOM ROUTES HERE
45 # CUSTOM ROUTES HERE
46 #==========================================================================
46 #==========================================================================
47
47
48 #MAIN PAGE
48 #MAIN PAGE
49 rmap.connect('home', '/', controller='home', action='index')
49 rmap.connect('home', '/', controller='home', action='index')
50 rmap.connect('repo_switcher', '/repos', controller='home',
50 rmap.connect('repo_switcher', '/repos', controller='home',
51 action='repo_switcher')
51 action='repo_switcher')
52 rmap.connect('bugtracker',
52 rmap.connect('bugtracker',
53 "http://bitbucket.org/marcinkuzminski/rhodecode/issues",
53 "http://bitbucket.org/marcinkuzminski/rhodecode/issues",
54 _static=True)
54 _static=True)
55 rmap.connect('rhodecode_official', "http://rhodecode.org", _static=True)
55 rmap.connect('rhodecode_official', "http://rhodecode.org", _static=True)
56
56
57 #ADMIN REPOSITORY REST ROUTES
57 #ADMIN REPOSITORY REST ROUTES
58 with rmap.submapper(path_prefix=ADMIN_PREFIX,
58 with rmap.submapper(path_prefix=ADMIN_PREFIX,
59 controller='admin/repos') as m:
59 controller='admin/repos') as m:
60 m.connect("repos", "/repos",
60 m.connect("repos", "/repos",
61 action="create", conditions=dict(method=["POST"]))
61 action="create", conditions=dict(method=["POST"]))
62 m.connect("repos", "/repos",
62 m.connect("repos", "/repos",
63 action="index", conditions=dict(method=["GET"]))
63 action="index", conditions=dict(method=["GET"]))
64 m.connect("formatted_repos", "/repos.{format}",
64 m.connect("formatted_repos", "/repos.{format}",
65 action="index",
65 action="index",
66 conditions=dict(method=["GET"]))
66 conditions=dict(method=["GET"]))
67 m.connect("new_repo", "/repos/new",
67 m.connect("new_repo", "/repos/new",
68 action="new", conditions=dict(method=["GET"]))
68 action="new", conditions=dict(method=["GET"]))
69 m.connect("formatted_new_repo", "/repos/new.{format}",
69 m.connect("formatted_new_repo", "/repos/new.{format}",
70 action="new", conditions=dict(method=["GET"]))
70 action="new", conditions=dict(method=["GET"]))
71 m.connect("/repos/{repo_name:.*}",
71 m.connect("/repos/{repo_name:.*}",
72 action="update", conditions=dict(method=["PUT"],
72 action="update", conditions=dict(method=["PUT"],
73 function=check_repo))
73 function=check_repo))
74 m.connect("/repos/{repo_name:.*}",
74 m.connect("/repos/{repo_name:.*}",
75 action="delete", conditions=dict(method=["DELETE"],
75 action="delete", conditions=dict(method=["DELETE"],
76 function=check_repo))
76 function=check_repo))
77 m.connect("edit_repo", "/repos/{repo_name:.*}/edit",
77 m.connect("edit_repo", "/repos/{repo_name:.*}/edit",
78 action="edit", conditions=dict(method=["GET"],
78 action="edit", conditions=dict(method=["GET"],
79 function=check_repo))
79 function=check_repo))
80 m.connect("formatted_edit_repo", "/repos/{repo_name:.*}.{format}/edit",
80 m.connect("formatted_edit_repo", "/repos/{repo_name:.*}.{format}/edit",
81 action="edit", conditions=dict(method=["GET"],
81 action="edit", conditions=dict(method=["GET"],
82 function=check_repo))
82 function=check_repo))
83 m.connect("repo", "/repos/{repo_name:.*}",
83 m.connect("repo", "/repos/{repo_name:.*}",
84 action="show", conditions=dict(method=["GET"],
84 action="show", conditions=dict(method=["GET"],
85 function=check_repo))
85 function=check_repo))
86 m.connect("formatted_repo", "/repos/{repo_name:.*}.{format}",
86 m.connect("formatted_repo", "/repos/{repo_name:.*}.{format}",
87 action="show", conditions=dict(method=["GET"],
87 action="show", conditions=dict(method=["GET"],
88 function=check_repo))
88 function=check_repo))
89 #ajax delete repo perm user
89 #ajax delete repo perm user
90 m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*}",
90 m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*}",
91 action="delete_perm_user", conditions=dict(method=["DELETE"],
91 action="delete_perm_user", conditions=dict(method=["DELETE"],
92 function=check_repo))
92 function=check_repo))
93 #ajax delete repo perm users_group
93 #ajax delete repo perm users_group
94 m.connect('delete_repo_users_group',
94 m.connect('delete_repo_users_group',
95 "/repos_delete_users_group/{repo_name:.*}",
95 "/repos_delete_users_group/{repo_name:.*}",
96 action="delete_perm_users_group",
96 action="delete_perm_users_group",
97 conditions=dict(method=["DELETE"], function=check_repo))
97 conditions=dict(method=["DELETE"], function=check_repo))
98
98
99 #settings actions
99 #settings actions
100 m.connect('repo_stats', "/repos_stats/{repo_name:.*}",
100 m.connect('repo_stats', "/repos_stats/{repo_name:.*}",
101 action="repo_stats", conditions=dict(method=["DELETE"],
101 action="repo_stats", conditions=dict(method=["DELETE"],
102 function=check_repo))
102 function=check_repo))
103 m.connect('repo_cache', "/repos_cache/{repo_name:.*}",
103 m.connect('repo_cache', "/repos_cache/{repo_name:.*}",
104 action="repo_cache", conditions=dict(method=["DELETE"],
104 action="repo_cache", conditions=dict(method=["DELETE"],
105 function=check_repo))
105 function=check_repo))
106 m.connect('repo_public_journal',
106 m.connect('repo_public_journal',
107 "/repos_public_journal/{repo_name:.*}",
107 "/repos_public_journal/{repo_name:.*}",
108 action="repo_public_journal", conditions=dict(method=["PUT"],
108 action="repo_public_journal", conditions=dict(method=["PUT"],
109 function=check_repo))
109 function=check_repo))
110 m.connect('repo_pull', "/repo_pull/{repo_name:.*}",
110 m.connect('repo_pull', "/repo_pull/{repo_name:.*}",
111 action="repo_pull", conditions=dict(method=["PUT"],
111 action="repo_pull", conditions=dict(method=["PUT"],
112 function=check_repo))
112 function=check_repo))
113
113
114 with rmap.submapper(path_prefix=ADMIN_PREFIX,
114 with rmap.submapper(path_prefix=ADMIN_PREFIX,
115 controller='admin/repos_groups') as m:
115 controller='admin/repos_groups') as m:
116 m.connect("repos_groups", "/repos_groups",
116 m.connect("repos_groups", "/repos_groups",
117 action="create", conditions=dict(method=["POST"]))
117 action="create", conditions=dict(method=["POST"]))
118 m.connect("repos_groups", "/repos_groups",
118 m.connect("repos_groups", "/repos_groups",
119 action="index", conditions=dict(method=["GET"]))
119 action="index", conditions=dict(method=["GET"]))
120 m.connect("formatted_repos_groups", "/repos_groups.{format}",
120 m.connect("formatted_repos_groups", "/repos_groups.{format}",
121 action="index", conditions=dict(method=["GET"]))
121 action="index", conditions=dict(method=["GET"]))
122 m.connect("new_repos_group", "/repos_groups/new",
122 m.connect("new_repos_group", "/repos_groups/new",
123 action="new", conditions=dict(method=["GET"]))
123 action="new", conditions=dict(method=["GET"]))
124 m.connect("formatted_new_repos_group", "/repos_groups/new.{format}",
124 m.connect("formatted_new_repos_group", "/repos_groups/new.{format}",
125 action="new", conditions=dict(method=["GET"]))
125 action="new", conditions=dict(method=["GET"]))
126 m.connect("update_repos_group", "/repos_groups/{id}",
126 m.connect("update_repos_group", "/repos_groups/{id}",
127 action="update", conditions=dict(method=["PUT"],
127 action="update", conditions=dict(method=["PUT"],
128 function=check_int))
128 function=check_int))
129 m.connect("delete_repos_group", "/repos_groups/{id}",
129 m.connect("delete_repos_group", "/repos_groups/{id}",
130 action="delete", conditions=dict(method=["DELETE"],
130 action="delete", conditions=dict(method=["DELETE"],
131 function=check_int))
131 function=check_int))
132 m.connect("edit_repos_group", "/repos_groups/{id}/edit",
132 m.connect("edit_repos_group", "/repos_groups/{id}/edit",
133 action="edit", conditions=dict(method=["GET"],
133 action="edit", conditions=dict(method=["GET"],
134 function=check_int))
134 function=check_int))
135 m.connect("formatted_edit_repos_group",
135 m.connect("formatted_edit_repos_group",
136 "/repos_groups/{id}.{format}/edit",
136 "/repos_groups/{id}.{format}/edit",
137 action="edit", conditions=dict(method=["GET"],
137 action="edit", conditions=dict(method=["GET"],
138 function=check_int))
138 function=check_int))
139 m.connect("repos_group", "/repos_groups/{id}",
139 m.connect("repos_group", "/repos_groups/{id}",
140 action="show", conditions=dict(method=["GET"],
140 action="show", conditions=dict(method=["GET"],
141 function=check_int))
141 function=check_int))
142 m.connect("formatted_repos_group", "/repos_groups/{id}.{format}",
142 m.connect("formatted_repos_group", "/repos_groups/{id}.{format}",
143 action="show", conditions=dict(method=["GET"],
143 action="show", conditions=dict(method=["GET"],
144 function=check_int))
144 function=check_int))
145
145
146 #ADMIN USER REST ROUTES
146 #ADMIN USER REST ROUTES
147 with rmap.submapper(path_prefix=ADMIN_PREFIX,
147 with rmap.submapper(path_prefix=ADMIN_PREFIX,
148 controller='admin/users') as m:
148 controller='admin/users') as m:
149 m.connect("users", "/users",
149 m.connect("users", "/users",
150 action="create", conditions=dict(method=["POST"]))
150 action="create", conditions=dict(method=["POST"]))
151 m.connect("users", "/users",
151 m.connect("users", "/users",
152 action="index", conditions=dict(method=["GET"]))
152 action="index", conditions=dict(method=["GET"]))
153 m.connect("formatted_users", "/users.{format}",
153 m.connect("formatted_users", "/users.{format}",
154 action="index", conditions=dict(method=["GET"]))
154 action="index", conditions=dict(method=["GET"]))
155 m.connect("new_user", "/users/new",
155 m.connect("new_user", "/users/new",
156 action="new", conditions=dict(method=["GET"]))
156 action="new", conditions=dict(method=["GET"]))
157 m.connect("formatted_new_user", "/users/new.{format}",
157 m.connect("formatted_new_user", "/users/new.{format}",
158 action="new", conditions=dict(method=["GET"]))
158 action="new", conditions=dict(method=["GET"]))
159 m.connect("update_user", "/users/{id}",
159 m.connect("update_user", "/users/{id}",
160 action="update", conditions=dict(method=["PUT"]))
160 action="update", conditions=dict(method=["PUT"]))
161 m.connect("delete_user", "/users/{id}",
161 m.connect("delete_user", "/users/{id}",
162 action="delete", conditions=dict(method=["DELETE"]))
162 action="delete", conditions=dict(method=["DELETE"]))
163 m.connect("edit_user", "/users/{id}/edit",
163 m.connect("edit_user", "/users/{id}/edit",
164 action="edit", conditions=dict(method=["GET"]))
164 action="edit", conditions=dict(method=["GET"]))
165 m.connect("formatted_edit_user",
165 m.connect("formatted_edit_user",
166 "/users/{id}.{format}/edit",
166 "/users/{id}.{format}/edit",
167 action="edit", conditions=dict(method=["GET"]))
167 action="edit", conditions=dict(method=["GET"]))
168 m.connect("user", "/users/{id}",
168 m.connect("user", "/users/{id}",
169 action="show", conditions=dict(method=["GET"]))
169 action="show", conditions=dict(method=["GET"]))
170 m.connect("formatted_user", "/users/{id}.{format}",
170 m.connect("formatted_user", "/users/{id}.{format}",
171 action="show", conditions=dict(method=["GET"]))
171 action="show", conditions=dict(method=["GET"]))
172
172
173 #EXTRAS USER ROUTES
173 #EXTRAS USER ROUTES
174 m.connect("user_perm", "/users_perm/{id}",
174 m.connect("user_perm", "/users_perm/{id}",
175 action="update_perm", conditions=dict(method=["PUT"]))
175 action="update_perm", conditions=dict(method=["PUT"]))
176
176
177 #ADMIN USERS REST ROUTES
177 #ADMIN USERS REST ROUTES
178 with rmap.submapper(path_prefix=ADMIN_PREFIX,
178 with rmap.submapper(path_prefix=ADMIN_PREFIX,
179 controller='admin/users_groups') as m:
179 controller='admin/users_groups') as m:
180 m.connect("users_groups", "/users_groups",
180 m.connect("users_groups", "/users_groups",
181 action="create", conditions=dict(method=["POST"]))
181 action="create", conditions=dict(method=["POST"]))
182 m.connect("users_groups", "/users_groups",
182 m.connect("users_groups", "/users_groups",
183 action="index", conditions=dict(method=["GET"]))
183 action="index", conditions=dict(method=["GET"]))
184 m.connect("formatted_users_groups", "/users_groups.{format}",
184 m.connect("formatted_users_groups", "/users_groups.{format}",
185 action="index", conditions=dict(method=["GET"]))
185 action="index", conditions=dict(method=["GET"]))
186 m.connect("new_users_group", "/users_groups/new",
186 m.connect("new_users_group", "/users_groups/new",
187 action="new", conditions=dict(method=["GET"]))
187 action="new", conditions=dict(method=["GET"]))
188 m.connect("formatted_new_users_group", "/users_groups/new.{format}",
188 m.connect("formatted_new_users_group", "/users_groups/new.{format}",
189 action="new", conditions=dict(method=["GET"]))
189 action="new", conditions=dict(method=["GET"]))
190 m.connect("update_users_group", "/users_groups/{id}",
190 m.connect("update_users_group", "/users_groups/{id}",
191 action="update", conditions=dict(method=["PUT"]))
191 action="update", conditions=dict(method=["PUT"]))
192 m.connect("delete_users_group", "/users_groups/{id}",
192 m.connect("delete_users_group", "/users_groups/{id}",
193 action="delete", conditions=dict(method=["DELETE"]))
193 action="delete", conditions=dict(method=["DELETE"]))
194 m.connect("edit_users_group", "/users_groups/{id}/edit",
194 m.connect("edit_users_group", "/users_groups/{id}/edit",
195 action="edit", conditions=dict(method=["GET"]))
195 action="edit", conditions=dict(method=["GET"]))
196 m.connect("formatted_edit_users_group",
196 m.connect("formatted_edit_users_group",
197 "/users_groups/{id}.{format}/edit",
197 "/users_groups/{id}.{format}/edit",
198 action="edit", conditions=dict(method=["GET"]))
198 action="edit", conditions=dict(method=["GET"]))
199 m.connect("users_group", "/users_groups/{id}",
199 m.connect("users_group", "/users_groups/{id}",
200 action="show", conditions=dict(method=["GET"]))
200 action="show", conditions=dict(method=["GET"]))
201 m.connect("formatted_users_group", "/users_groups/{id}.{format}",
201 m.connect("formatted_users_group", "/users_groups/{id}.{format}",
202 action="show", conditions=dict(method=["GET"]))
202 action="show", conditions=dict(method=["GET"]))
203
203
204 #EXTRAS USER ROUTES
204 #EXTRAS USER ROUTES
205 m.connect("users_group_perm", "/users_groups_perm/{id}",
205 m.connect("users_group_perm", "/users_groups_perm/{id}",
206 action="update_perm", conditions=dict(method=["PUT"]))
206 action="update_perm", conditions=dict(method=["PUT"]))
207
207
208 #ADMIN GROUP REST ROUTES
208 #ADMIN GROUP REST ROUTES
209 rmap.resource('group', 'groups',
209 rmap.resource('group', 'groups',
210 controller='admin/groups', path_prefix=ADMIN_PREFIX)
210 controller='admin/groups', path_prefix=ADMIN_PREFIX)
211
211
212 #ADMIN PERMISSIONS REST ROUTES
212 #ADMIN PERMISSIONS REST ROUTES
213 rmap.resource('permission', 'permissions',
213 rmap.resource('permission', 'permissions',
214 controller='admin/permissions', path_prefix=ADMIN_PREFIX)
214 controller='admin/permissions', path_prefix=ADMIN_PREFIX)
215
215
216 ##ADMIN LDAP SETTINGS
216 ##ADMIN LDAP SETTINGS
217 rmap.connect('ldap_settings', '%s/ldap' % ADMIN_PREFIX,
217 rmap.connect('ldap_settings', '%s/ldap' % ADMIN_PREFIX,
218 controller='admin/ldap_settings', action='ldap_settings',
218 controller='admin/ldap_settings', action='ldap_settings',
219 conditions=dict(method=["POST"]))
219 conditions=dict(method=["POST"]))
220
220
221 rmap.connect('ldap_home', '%s/ldap' % ADMIN_PREFIX,
221 rmap.connect('ldap_home', '%s/ldap' % ADMIN_PREFIX,
222 controller='admin/ldap_settings')
222 controller='admin/ldap_settings')
223
223
224 #ADMIN SETTINGS REST ROUTES
224 #ADMIN SETTINGS REST ROUTES
225 with rmap.submapper(path_prefix=ADMIN_PREFIX,
225 with rmap.submapper(path_prefix=ADMIN_PREFIX,
226 controller='admin/settings') as m:
226 controller='admin/settings') as m:
227 m.connect("admin_settings", "/settings",
227 m.connect("admin_settings", "/settings",
228 action="create", conditions=dict(method=["POST"]))
228 action="create", conditions=dict(method=["POST"]))
229 m.connect("admin_settings", "/settings",
229 m.connect("admin_settings", "/settings",
230 action="index", conditions=dict(method=["GET"]))
230 action="index", conditions=dict(method=["GET"]))
231 m.connect("formatted_admin_settings", "/settings.{format}",
231 m.connect("formatted_admin_settings", "/settings.{format}",
232 action="index", conditions=dict(method=["GET"]))
232 action="index", conditions=dict(method=["GET"]))
233 m.connect("admin_new_setting", "/settings/new",
233 m.connect("admin_new_setting", "/settings/new",
234 action="new", conditions=dict(method=["GET"]))
234 action="new", conditions=dict(method=["GET"]))
235 m.connect("formatted_admin_new_setting", "/settings/new.{format}",
235 m.connect("formatted_admin_new_setting", "/settings/new.{format}",
236 action="new", conditions=dict(method=["GET"]))
236 action="new", conditions=dict(method=["GET"]))
237 m.connect("/settings/{setting_id}",
237 m.connect("/settings/{setting_id}",
238 action="update", conditions=dict(method=["PUT"]))
238 action="update", conditions=dict(method=["PUT"]))
239 m.connect("/settings/{setting_id}",
239 m.connect("/settings/{setting_id}",
240 action="delete", conditions=dict(method=["DELETE"]))
240 action="delete", conditions=dict(method=["DELETE"]))
241 m.connect("admin_edit_setting", "/settings/{setting_id}/edit",
241 m.connect("admin_edit_setting", "/settings/{setting_id}/edit",
242 action="edit", conditions=dict(method=["GET"]))
242 action="edit", conditions=dict(method=["GET"]))
243 m.connect("formatted_admin_edit_setting",
243 m.connect("formatted_admin_edit_setting",
244 "/settings/{setting_id}.{format}/edit",
244 "/settings/{setting_id}.{format}/edit",
245 action="edit", conditions=dict(method=["GET"]))
245 action="edit", conditions=dict(method=["GET"]))
246 m.connect("admin_setting", "/settings/{setting_id}",
246 m.connect("admin_setting", "/settings/{setting_id}",
247 action="show", conditions=dict(method=["GET"]))
247 action="show", conditions=dict(method=["GET"]))
248 m.connect("formatted_admin_setting", "/settings/{setting_id}.{format}",
248 m.connect("formatted_admin_setting", "/settings/{setting_id}.{format}",
249 action="show", conditions=dict(method=["GET"]))
249 action="show", conditions=dict(method=["GET"]))
250 m.connect("admin_settings_my_account", "/my_account",
250 m.connect("admin_settings_my_account", "/my_account",
251 action="my_account", conditions=dict(method=["GET"]))
251 action="my_account", conditions=dict(method=["GET"]))
252 m.connect("admin_settings_my_account_update", "/my_account_update",
252 m.connect("admin_settings_my_account_update", "/my_account_update",
253 action="my_account_update", conditions=dict(method=["PUT"]))
253 action="my_account_update", conditions=dict(method=["PUT"]))
254 m.connect("admin_settings_create_repository", "/create_repository",
254 m.connect("admin_settings_create_repository", "/create_repository",
255 action="create_repository", conditions=dict(method=["GET"]))
255 action="create_repository", conditions=dict(method=["GET"]))
256
256
257 #ADMIN MAIN PAGES
257 #ADMIN MAIN PAGES
258 with rmap.submapper(path_prefix=ADMIN_PREFIX,
258 with rmap.submapper(path_prefix=ADMIN_PREFIX,
259 controller='admin/admin') as m:
259 controller='admin/admin') as m:
260 m.connect('admin_home', '', action='index')
260 m.connect('admin_home', '', action='index')
261 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
261 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
262 action='add_repo')
262 action='add_repo')
263
263
264 #USER JOURNAL
264 #USER JOURNAL
265 rmap.connect('journal', '%s/journal' % ADMIN_PREFIX, controller='journal')
265 rmap.connect('journal', '%s/journal' % ADMIN_PREFIX, controller='journal')
266
266
267 rmap.connect('public_journal', '%s/public_journal' % ADMIN_PREFIX,
267 rmap.connect('public_journal', '%s/public_journal' % ADMIN_PREFIX,
268 controller='journal', action="public_journal")
268 controller='journal', action="public_journal")
269
269
270 rmap.connect('public_journal_rss', '%s/public_journal_rss' % ADMIN_PREFIX,
270 rmap.connect('public_journal_rss', '%s/public_journal_rss' % ADMIN_PREFIX,
271 controller='journal', action="public_journal_rss")
271 controller='journal', action="public_journal_rss")
272
272
273 rmap.connect('public_journal_atom',
273 rmap.connect('public_journal_atom',
274 '%s/public_journal_atom' % ADMIN_PREFIX, controller='journal',
274 '%s/public_journal_atom' % ADMIN_PREFIX, controller='journal',
275 action="public_journal_atom")
275 action="public_journal_atom")
276
276
277 rmap.connect('toggle_following', '%s/toggle_following' % ADMIN_PREFIX,
277 rmap.connect('toggle_following', '%s/toggle_following' % ADMIN_PREFIX,
278 controller='journal', action='toggle_following',
278 controller='journal', action='toggle_following',
279 conditions=dict(method=["POST"]))
279 conditions=dict(method=["POST"]))
280
280
281 #SEARCH
281 #SEARCH
282 rmap.connect('search', '%s/search' % ADMIN_PREFIX, controller='search',)
282 rmap.connect('search', '%s/search' % ADMIN_PREFIX, controller='search',)
283 rmap.connect('search_repo', '%s/search/{search_repo:.*}' % ADMIN_PREFIX,
283 rmap.connect('search_repo', '%s/search/{search_repo:.*}' % ADMIN_PREFIX,
284 controller='search')
284 controller='search')
285
285
286 #LOGIN/LOGOUT/REGISTER/SIGN IN
286 #LOGIN/LOGOUT/REGISTER/SIGN IN
287 rmap.connect('login_home', '%s/login' % ADMIN_PREFIX, controller='login')
287 rmap.connect('login_home', '%s/login' % ADMIN_PREFIX, controller='login')
288 rmap.connect('logout_home', '%s/logout' % ADMIN_PREFIX, controller='login',
288 rmap.connect('logout_home', '%s/logout' % ADMIN_PREFIX, controller='login',
289 action='logout')
289 action='logout')
290
290
291 rmap.connect('register', '%s/register' % ADMIN_PREFIX, controller='login',
291 rmap.connect('register', '%s/register' % ADMIN_PREFIX, controller='login',
292 action='register')
292 action='register')
293
293
294 rmap.connect('reset_password', '%s/password_reset' % ADMIN_PREFIX,
294 rmap.connect('reset_password', '%s/password_reset' % ADMIN_PREFIX,
295 controller='login', action='password_reset')
295 controller='login', action='password_reset')
296
296
297 rmap.connect('reset_password_confirmation',
298 '%s/password_reset_confirmation' % ADMIN_PREFIX,
299 controller='login', action='password_reset_confirmation')
300
297 #FEEDS
301 #FEEDS
298 rmap.connect('rss_feed_home', '/{repo_name:.*}/feed/rss',
302 rmap.connect('rss_feed_home', '/{repo_name:.*}/feed/rss',
299 controller='feed', action='rss',
303 controller='feed', action='rss',
300 conditions=dict(function=check_repo))
304 conditions=dict(function=check_repo))
301
305
302 rmap.connect('atom_feed_home', '/{repo_name:.*}/feed/atom',
306 rmap.connect('atom_feed_home', '/{repo_name:.*}/feed/atom',
303 controller='feed', action='atom',
307 controller='feed', action='atom',
304 conditions=dict(function=check_repo))
308 conditions=dict(function=check_repo))
305
309
306 #==========================================================================
310 #==========================================================================
307 # REPOSITORY ROUTES
311 # REPOSITORY ROUTES
308 #==========================================================================
312 #==========================================================================
309 rmap.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
313 rmap.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
310 controller='changeset', revision='tip',
314 controller='changeset', revision='tip',
311 conditions=dict(function=check_repo))
315 conditions=dict(function=check_repo))
312
316
313 rmap.connect('raw_changeset_home',
317 rmap.connect('raw_changeset_home',
314 '/{repo_name:.*}/raw-changeset/{revision}',
318 '/{repo_name:.*}/raw-changeset/{revision}',
315 controller='changeset', action='raw_changeset',
319 controller='changeset', action='raw_changeset',
316 revision='tip', conditions=dict(function=check_repo))
320 revision='tip', conditions=dict(function=check_repo))
317
321
318 rmap.connect('summary_home', '/{repo_name:.*}',
322 rmap.connect('summary_home', '/{repo_name:.*}',
319 controller='summary', conditions=dict(function=check_repo))
323 controller='summary', conditions=dict(function=check_repo))
320
324
321 rmap.connect('summary_home', '/{repo_name:.*}/summary',
325 rmap.connect('summary_home', '/{repo_name:.*}/summary',
322 controller='summary', conditions=dict(function=check_repo))
326 controller='summary', conditions=dict(function=check_repo))
323
327
324 rmap.connect('shortlog_home', '/{repo_name:.*}/shortlog',
328 rmap.connect('shortlog_home', '/{repo_name:.*}/shortlog',
325 controller='shortlog', conditions=dict(function=check_repo))
329 controller='shortlog', conditions=dict(function=check_repo))
326
330
327 rmap.connect('branches_home', '/{repo_name:.*}/branches',
331 rmap.connect('branches_home', '/{repo_name:.*}/branches',
328 controller='branches', conditions=dict(function=check_repo))
332 controller='branches', conditions=dict(function=check_repo))
329
333
330 rmap.connect('tags_home', '/{repo_name:.*}/tags',
334 rmap.connect('tags_home', '/{repo_name:.*}/tags',
331 controller='tags', conditions=dict(function=check_repo))
335 controller='tags', conditions=dict(function=check_repo))
332
336
333 rmap.connect('changelog_home', '/{repo_name:.*}/changelog',
337 rmap.connect('changelog_home', '/{repo_name:.*}/changelog',
334 controller='changelog', conditions=dict(function=check_repo))
338 controller='changelog', conditions=dict(function=check_repo))
335
339
336 rmap.connect('files_home', '/{repo_name:.*}/files/{revision}/{f_path:.*}',
340 rmap.connect('files_home', '/{repo_name:.*}/files/{revision}/{f_path:.*}',
337 controller='files', revision='tip', f_path='',
341 controller='files', revision='tip', f_path='',
338 conditions=dict(function=check_repo))
342 conditions=dict(function=check_repo))
339
343
340 rmap.connect('files_diff_home', '/{repo_name:.*}/diff/{f_path:.*}',
344 rmap.connect('files_diff_home', '/{repo_name:.*}/diff/{f_path:.*}',
341 controller='files', action='diff', revision='tip', f_path='',
345 controller='files', action='diff', revision='tip', f_path='',
342 conditions=dict(function=check_repo))
346 conditions=dict(function=check_repo))
343
347
344 rmap.connect('files_rawfile_home',
348 rmap.connect('files_rawfile_home',
345 '/{repo_name:.*}/rawfile/{revision}/{f_path:.*}',
349 '/{repo_name:.*}/rawfile/{revision}/{f_path:.*}',
346 controller='files', action='rawfile', revision='tip',
350 controller='files', action='rawfile', revision='tip',
347 f_path='', conditions=dict(function=check_repo))
351 f_path='', conditions=dict(function=check_repo))
348
352
349 rmap.connect('files_raw_home',
353 rmap.connect('files_raw_home',
350 '/{repo_name:.*}/raw/{revision}/{f_path:.*}',
354 '/{repo_name:.*}/raw/{revision}/{f_path:.*}',
351 controller='files', action='raw', revision='tip', f_path='',
355 controller='files', action='raw', revision='tip', f_path='',
352 conditions=dict(function=check_repo))
356 conditions=dict(function=check_repo))
353
357
354 rmap.connect('files_annotate_home',
358 rmap.connect('files_annotate_home',
355 '/{repo_name:.*}/annotate/{revision}/{f_path:.*}',
359 '/{repo_name:.*}/annotate/{revision}/{f_path:.*}',
356 controller='files', action='annotate', revision='tip',
360 controller='files', action='annotate', revision='tip',
357 f_path='', conditions=dict(function=check_repo))
361 f_path='', conditions=dict(function=check_repo))
358
362
359 rmap.connect('files_edit_home',
363 rmap.connect('files_edit_home',
360 '/{repo_name:.*}/edit/{revision}/{f_path:.*}',
364 '/{repo_name:.*}/edit/{revision}/{f_path:.*}',
361 controller='files', action='edit', revision='tip',
365 controller='files', action='edit', revision='tip',
362 f_path='', conditions=dict(function=check_repo))
366 f_path='', conditions=dict(function=check_repo))
363
367
364 rmap.connect('files_archive_home', '/{repo_name:.*}/archive/{fname}',
368 rmap.connect('files_archive_home', '/{repo_name:.*}/archive/{fname}',
365 controller='files', action='archivefile',
369 controller='files', action='archivefile',
366 conditions=dict(function=check_repo))
370 conditions=dict(function=check_repo))
367
371
368 rmap.connect('repo_settings_delete', '/{repo_name:.*}/settings',
372 rmap.connect('repo_settings_delete', '/{repo_name:.*}/settings',
369 controller='settings', action="delete",
373 controller='settings', action="delete",
370 conditions=dict(method=["DELETE"], function=check_repo))
374 conditions=dict(method=["DELETE"], function=check_repo))
371
375
372 rmap.connect('repo_settings_update', '/{repo_name:.*}/settings',
376 rmap.connect('repo_settings_update', '/{repo_name:.*}/settings',
373 controller='settings', action="update",
377 controller='settings', action="update",
374 conditions=dict(method=["PUT"], function=check_repo))
378 conditions=dict(method=["PUT"], function=check_repo))
375
379
376 rmap.connect('repo_settings_home', '/{repo_name:.*}/settings',
380 rmap.connect('repo_settings_home', '/{repo_name:.*}/settings',
377 controller='settings', action='index',
381 controller='settings', action='index',
378 conditions=dict(function=check_repo))
382 conditions=dict(function=check_repo))
379
383
380 rmap.connect('repo_fork_create_home', '/{repo_name:.*}/fork',
384 rmap.connect('repo_fork_create_home', '/{repo_name:.*}/fork',
381 controller='settings', action='fork_create',
385 controller='settings', action='fork_create',
382 conditions=dict(function=check_repo, method=["POST"]))
386 conditions=dict(function=check_repo, method=["POST"]))
383
387
384 rmap.connect('repo_fork_home', '/{repo_name:.*}/fork',
388 rmap.connect('repo_fork_home', '/{repo_name:.*}/fork',
385 controller='settings', action='fork',
389 controller='settings', action='fork',
386 conditions=dict(function=check_repo))
390 conditions=dict(function=check_repo))
387
391
388 rmap.connect('repo_followers_home', '/{repo_name:.*}/followers',
392 rmap.connect('repo_followers_home', '/{repo_name:.*}/followers',
389 controller='followers', action='followers',
393 controller='followers', action='followers',
390 conditions=dict(function=check_repo))
394 conditions=dict(function=check_repo))
391
395
392 rmap.connect('repo_forks_home', '/{repo_name:.*}/forks',
396 rmap.connect('repo_forks_home', '/{repo_name:.*}/forks',
393 controller='forks', action='forks',
397 controller='forks', action='forks',
394 conditions=dict(function=check_repo))
398 conditions=dict(function=check_repo))
395 return rmap
399 return rmap
@@ -1,151 +1,168 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.login
3 rhodecode.controllers.login
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Login controller for rhodeocode
6 Login controller for rhodeocode
7
7
8 :created_on: Apr 22, 2010
8 :created_on: Apr 22, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import formencode
27 import formencode
28
28
29 from formencode import htmlfill
29 from formencode import htmlfill
30
30
31 from pylons.i18n.translation import _
31 from pylons.i18n.translation import _
32 from pylons.controllers.util import abort, redirect
32 from pylons.controllers.util import abort, redirect
33 from pylons import request, response, session, tmpl_context as c, url
33 from pylons import request, response, session, tmpl_context as c, url
34
34
35 import rhodecode.lib.helpers as h
35 import rhodecode.lib.helpers as h
36 from rhodecode.lib.auth import AuthUser, HasPermissionAnyDecorator
36 from rhodecode.lib.auth import AuthUser, HasPermissionAnyDecorator
37 from rhodecode.lib.base import BaseController, render
37 from rhodecode.lib.base import BaseController, render
38 from rhodecode.model.db import User
38 from rhodecode.model.db import User
39 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
39 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
40 from rhodecode.model.user import UserModel
40 from rhodecode.model.user import UserModel
41
41
42
42
43 log = logging.getLogger(__name__)
43 log = logging.getLogger(__name__)
44
44
45
45
46 class LoginController(BaseController):
46 class LoginController(BaseController):
47
47
48 def __before__(self):
48 def __before__(self):
49 super(LoginController, self).__before__()
49 super(LoginController, self).__before__()
50
50
51 def index(self):
51 def index(self):
52 #redirect if already logged in
52 #redirect if already logged in
53 c.came_from = request.GET.get('came_from', None)
53 c.came_from = request.GET.get('came_from', None)
54
54
55 if self.rhodecode_user.is_authenticated \
55 if self.rhodecode_user.is_authenticated \
56 and self.rhodecode_user.username != 'default':
56 and self.rhodecode_user.username != 'default':
57
57
58 return redirect(url('home'))
58 return redirect(url('home'))
59
59
60 if request.POST:
60 if request.POST:
61 #import Login Form validator class
61 #import Login Form validator class
62 login_form = LoginForm()
62 login_form = LoginForm()
63 try:
63 try:
64 c.form_result = login_form.to_python(dict(request.POST))
64 c.form_result = login_form.to_python(dict(request.POST))
65 #form checks for username/password, now we're authenticated
65 #form checks for username/password, now we're authenticated
66 username = c.form_result['username']
66 username = c.form_result['username']
67 user = User.by_username(username,
67 user = User.by_username(username,
68 case_insensitive=True)
68 case_insensitive=True)
69 auth_user = AuthUser(user.user_id)
69 auth_user = AuthUser(user.user_id)
70 auth_user.set_authenticated()
70 auth_user.set_authenticated()
71 session['rhodecode_user'] = auth_user
71 session['rhodecode_user'] = auth_user
72 session.save()
72 session.save()
73
73
74 log.info('user %s is now authenticated and stored in session',
74 log.info('user %s is now authenticated and stored in session',
75 username)
75 username)
76 user.update_lastlogin()
76 user.update_lastlogin()
77
77
78 if c.came_from:
78 if c.came_from:
79 return redirect(c.came_from)
79 return redirect(c.came_from)
80 else:
80 else:
81 return redirect(url('home'))
81 return redirect(url('home'))
82
82
83 except formencode.Invalid, errors:
83 except formencode.Invalid, errors:
84 return htmlfill.render(
84 return htmlfill.render(
85 render('/login.html'),
85 render('/login.html'),
86 defaults=errors.value,
86 defaults=errors.value,
87 errors=errors.error_dict or {},
87 errors=errors.error_dict or {},
88 prefix_error=False,
88 prefix_error=False,
89 encoding="UTF-8")
89 encoding="UTF-8")
90
90
91 return render('/login.html')
91 return render('/login.html')
92
92
93 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
93 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
94 'hg.register.manual_activate')
94 'hg.register.manual_activate')
95 def register(self):
95 def register(self):
96 user_model = UserModel()
96 user_model = UserModel()
97 c.auto_active = False
97 c.auto_active = False
98 for perm in user_model.get_by_username('default',
98 for perm in user_model.get_by_username('default',
99 cache=False).user_perms:
99 cache=False).user_perms:
100 if perm.permission.permission_name == 'hg.register.auto_activate':
100 if perm.permission.permission_name == 'hg.register.auto_activate':
101 c.auto_active = True
101 c.auto_active = True
102 break
102 break
103
103
104 if request.POST:
104 if request.POST:
105
105
106 register_form = RegisterForm()()
106 register_form = RegisterForm()()
107 try:
107 try:
108 form_result = register_form.to_python(dict(request.POST))
108 form_result = register_form.to_python(dict(request.POST))
109 form_result['active'] = c.auto_active
109 form_result['active'] = c.auto_active
110 user_model.create_registration(form_result)
110 user_model.create_registration(form_result)
111 h.flash(_('You have successfully registered into rhodecode'),
111 h.flash(_('You have successfully registered into rhodecode'),
112 category='success')
112 category='success')
113 return redirect(url('login_home'))
113 return redirect(url('login_home'))
114
114
115 except formencode.Invalid, errors:
115 except formencode.Invalid, errors:
116 return htmlfill.render(
116 return htmlfill.render(
117 render('/register.html'),
117 render('/register.html'),
118 defaults=errors.value,
118 defaults=errors.value,
119 errors=errors.error_dict or {},
119 errors=errors.error_dict or {},
120 prefix_error=False,
120 prefix_error=False,
121 encoding="UTF-8")
121 encoding="UTF-8")
122
122
123 return render('/register.html')
123 return render('/register.html')
124
124
125 def password_reset(self):
125 def password_reset(self):
126 user_model = UserModel()
126 user_model = UserModel()
127 if request.POST:
127 if request.POST:
128
128
129 password_reset_form = PasswordResetForm()()
129 password_reset_form = PasswordResetForm()()
130 try:
130 try:
131 form_result = password_reset_form.to_python(dict(request.POST))
131 form_result = password_reset_form.to_python(dict(request.POST))
132 user_model.reset_password(form_result)
132 user_model.reset_password_link(form_result)
133 h.flash(_('Your new password was sent'),
133 h.flash(_('Your password reset link was sent'),
134 category='success')
134 category='success')
135 return redirect(url('login_home'))
135 return redirect(url('login_home'))
136
136
137 except formencode.Invalid, errors:
137 except formencode.Invalid, errors:
138 return htmlfill.render(
138 return htmlfill.render(
139 render('/password_reset.html'),
139 render('/password_reset.html'),
140 defaults=errors.value,
140 defaults=errors.value,
141 errors=errors.error_dict or {},
141 errors=errors.error_dict or {},
142 prefix_error=False,
142 prefix_error=False,
143 encoding="UTF-8")
143 encoding="UTF-8")
144
144
145 return render('/password_reset.html')
145 return render('/password_reset.html')
146
146
147 def password_reset_confirmation(self):
148
149 if request.GET and request.GET.get('key'):
150 try:
151 user_model = UserModel()
152 user = User.get_by_api_key(request.GET.get('key'))
153 data = dict(email=user.email)
154 user_model.reset_password(data)
155 h.flash(_('Your password reset was successful, '
156 'new password has been sent to your email'),
157 category='success')
158 except Exception, e:
159 log.error(e)
160 return redirect(url('reset_password'))
161
162 return redirect(url('login_home'))
163
147 def logout(self):
164 def logout(self):
148 del session['rhodecode_user']
165 del session['rhodecode_user']
149 session.save()
166 session.save()
150 log.info('Logging out and setting user as Empty')
167 log.info('Logging out and setting user as Empty')
151 redirect(url('home'))
168 redirect(url('home'))
@@ -1,374 +1,413 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.celerylib.tasks
3 rhodecode.lib.celerylib.tasks
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 RhodeCode task modules, containing all task that suppose to be run
6 RhodeCode task modules, containing all task that suppose to be run
7 by celery daemon
7 by celery daemon
8
8
9 :created_on: Oct 6, 2010
9 :created_on: Oct 6, 2010
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 from celery.decorators import task
26 from celery.decorators import task
27
27
28 import os
28 import os
29 import traceback
29 import traceback
30 import logging
30 import logging
31 from os.path import dirname as dn, join as jn
31 from os.path import dirname as dn, join as jn
32
32
33 from time import mktime
33 from time import mktime
34 from operator import itemgetter
34 from operator import itemgetter
35 from string import lower
35 from string import lower
36
36
37 from pylons import config
37 from pylons import config, url
38 from pylons.i18n.translation import _
38 from pylons.i18n.translation import _
39
39
40 from rhodecode.lib import LANGUAGES_EXTENSIONS_MAP, safe_str
40 from rhodecode.lib import LANGUAGES_EXTENSIONS_MAP, safe_str
41 from rhodecode.lib.celerylib import run_task, locked_task, str2bool, \
41 from rhodecode.lib.celerylib import run_task, locked_task, str2bool, \
42 __get_lockkey, LockHeld, DaemonLock
42 __get_lockkey, LockHeld, DaemonLock
43 from rhodecode.lib.helpers import person
43 from rhodecode.lib.helpers import person
44 from rhodecode.lib.smtp_mailer import SmtpMailer
44 from rhodecode.lib.smtp_mailer import SmtpMailer
45 from rhodecode.lib.utils import add_cache
45 from rhodecode.lib.utils import add_cache
46 from rhodecode.lib.odict import OrderedDict
46 from rhodecode.lib.odict import OrderedDict
47 from rhodecode.model import init_model
47 from rhodecode.model import init_model
48 from rhodecode.model import meta
48 from rhodecode.model import meta
49 from rhodecode.model.db import RhodeCodeUi, Statistics, Repository
49 from rhodecode.model.db import RhodeCodeUi, Statistics, Repository
50
50
51 from vcs.backends import get_repo
51 from vcs.backends import get_repo
52
52
53 from sqlalchemy import engine_from_config
53 from sqlalchemy import engine_from_config
54
54
55 add_cache(config)
55 add_cache(config)
56
56
57 try:
57 try:
58 import json
58 import json
59 except ImportError:
59 except ImportError:
60 #python 2.5 compatibility
60 #python 2.5 compatibility
61 import simplejson as json
61 import simplejson as json
62
62
63 __all__ = ['whoosh_index', 'get_commits_stats',
63 __all__ = ['whoosh_index', 'get_commits_stats',
64 'reset_user_password', 'send_email']
64 'reset_user_password', 'send_email']
65
65
66 CELERY_ON = str2bool(config['app_conf'].get('use_celery'))
66 CELERY_ON = str2bool(config['app_conf'].get('use_celery'))
67
67
68
68
69 def get_session():
69 def get_session():
70 if CELERY_ON:
70 if CELERY_ON:
71 engine = engine_from_config(config, 'sqlalchemy.db1.')
71 engine = engine_from_config(config, 'sqlalchemy.db1.')
72 init_model(engine)
72 init_model(engine)
73 sa = meta.Session()
73 sa = meta.Session()
74 return sa
74 return sa
75
75
76
76
77 def get_repos_path():
77 def get_repos_path():
78 sa = get_session()
78 sa = get_session()
79 q = sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
79 q = sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
80 return q.ui_value
80 return q.ui_value
81
81
82
82
83 @task(ignore_result=True)
83 @task(ignore_result=True)
84 @locked_task
84 @locked_task
85 def whoosh_index(repo_location, full_index):
85 def whoosh_index(repo_location, full_index):
86 #log = whoosh_index.get_logger()
86 #log = whoosh_index.get_logger()
87 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
87 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
88 index_location = config['index_dir']
88 index_location = config['index_dir']
89 WhooshIndexingDaemon(index_location=index_location,
89 WhooshIndexingDaemon(index_location=index_location,
90 repo_location=repo_location, sa=get_session())\
90 repo_location=repo_location, sa=get_session())\
91 .run(full_index=full_index)
91 .run(full_index=full_index)
92
92
93
93
94 @task(ignore_result=True)
94 @task(ignore_result=True)
95 def get_commits_stats(repo_name, ts_min_y, ts_max_y):
95 def get_commits_stats(repo_name, ts_min_y, ts_max_y):
96 try:
96 try:
97 log = get_commits_stats.get_logger()
97 log = get_commits_stats.get_logger()
98 except:
98 except:
99 log = logging.getLogger(__name__)
99 log = logging.getLogger(__name__)
100
100
101 lockkey = __get_lockkey('get_commits_stats', repo_name, ts_min_y,
101 lockkey = __get_lockkey('get_commits_stats', repo_name, ts_min_y,
102 ts_max_y)
102 ts_max_y)
103 lockkey_path = dn(dn(dn(dn(os.path.abspath(__file__)))))
103 lockkey_path = dn(dn(dn(dn(os.path.abspath(__file__)))))
104 log.info('running task with lockkey %s', lockkey)
104 log.info('running task with lockkey %s', lockkey)
105 try:
105 try:
106 lock = l = DaemonLock(jn(lockkey_path, lockkey))
106 lock = l = DaemonLock(jn(lockkey_path, lockkey))
107
107
108 #for js data compatibilty cleans the key for person from '
108 #for js data compatibilty cleans the key for person from '
109 akc = lambda k: person(k).replace('"', "")
109 akc = lambda k: person(k).replace('"', "")
110
110
111 co_day_auth_aggr = {}
111 co_day_auth_aggr = {}
112 commits_by_day_aggregate = {}
112 commits_by_day_aggregate = {}
113 repos_path = get_repos_path()
113 repos_path = get_repos_path()
114 repo = get_repo(safe_str(os.path.join(repos_path, repo_name)))
114 repo = get_repo(safe_str(os.path.join(repos_path, repo_name)))
115 repo_size = len(repo.revisions)
115 repo_size = len(repo.revisions)
116 #return if repo have no revisions
116 #return if repo have no revisions
117 if repo_size < 1:
117 if repo_size < 1:
118 lock.release()
118 lock.release()
119 return True
119 return True
120
120
121 skip_date_limit = True
121 skip_date_limit = True
122 parse_limit = int(config['app_conf'].get('commit_parse_limit'))
122 parse_limit = int(config['app_conf'].get('commit_parse_limit'))
123 last_rev = 0
123 last_rev = 0
124 last_cs = None
124 last_cs = None
125 timegetter = itemgetter('time')
125 timegetter = itemgetter('time')
126
126
127 sa = get_session()
127 sa = get_session()
128
128
129 dbrepo = sa.query(Repository)\
129 dbrepo = sa.query(Repository)\
130 .filter(Repository.repo_name == repo_name).scalar()
130 .filter(Repository.repo_name == repo_name).scalar()
131 cur_stats = sa.query(Statistics)\
131 cur_stats = sa.query(Statistics)\
132 .filter(Statistics.repository == dbrepo).scalar()
132 .filter(Statistics.repository == dbrepo).scalar()
133
133
134 if cur_stats is not None:
134 if cur_stats is not None:
135 last_rev = cur_stats.stat_on_revision
135 last_rev = cur_stats.stat_on_revision
136
136
137 if last_rev == repo.get_changeset().revision and repo_size > 1:
137 if last_rev == repo.get_changeset().revision and repo_size > 1:
138 #pass silently without any work if we're not on first revision or
138 #pass silently without any work if we're not on first revision or
139 #current state of parsing revision(from db marker) is the
139 #current state of parsing revision(from db marker) is the
140 #last revision
140 #last revision
141 lock.release()
141 lock.release()
142 return True
142 return True
143
143
144 if cur_stats:
144 if cur_stats:
145 commits_by_day_aggregate = OrderedDict(json.loads(
145 commits_by_day_aggregate = OrderedDict(json.loads(
146 cur_stats.commit_activity_combined))
146 cur_stats.commit_activity_combined))
147 co_day_auth_aggr = json.loads(cur_stats.commit_activity)
147 co_day_auth_aggr = json.loads(cur_stats.commit_activity)
148
148
149 log.debug('starting parsing %s', parse_limit)
149 log.debug('starting parsing %s', parse_limit)
150 lmktime = mktime
150 lmktime = mktime
151
151
152 last_rev = last_rev + 1 if last_rev > 0 else last_rev
152 last_rev = last_rev + 1 if last_rev > 0 else last_rev
153
153
154 for cs in repo[last_rev:last_rev + parse_limit]:
154 for cs in repo[last_rev:last_rev + parse_limit]:
155 last_cs = cs # remember last parsed changeset
155 last_cs = cs # remember last parsed changeset
156 k = lmktime([cs.date.timetuple()[0], cs.date.timetuple()[1],
156 k = lmktime([cs.date.timetuple()[0], cs.date.timetuple()[1],
157 cs.date.timetuple()[2], 0, 0, 0, 0, 0, 0])
157 cs.date.timetuple()[2], 0, 0, 0, 0, 0, 0])
158
158
159 if akc(cs.author) in co_day_auth_aggr:
159 if akc(cs.author) in co_day_auth_aggr:
160 try:
160 try:
161 l = [timegetter(x) for x in
161 l = [timegetter(x) for x in
162 co_day_auth_aggr[akc(cs.author)]['data']]
162 co_day_auth_aggr[akc(cs.author)]['data']]
163 time_pos = l.index(k)
163 time_pos = l.index(k)
164 except ValueError:
164 except ValueError:
165 time_pos = False
165 time_pos = False
166
166
167 if time_pos >= 0 and time_pos is not False:
167 if time_pos >= 0 and time_pos is not False:
168
168
169 datadict = \
169 datadict = \
170 co_day_auth_aggr[akc(cs.author)]['data'][time_pos]
170 co_day_auth_aggr[akc(cs.author)]['data'][time_pos]
171
171
172 datadict["commits"] += 1
172 datadict["commits"] += 1
173 datadict["added"] += len(cs.added)
173 datadict["added"] += len(cs.added)
174 datadict["changed"] += len(cs.changed)
174 datadict["changed"] += len(cs.changed)
175 datadict["removed"] += len(cs.removed)
175 datadict["removed"] += len(cs.removed)
176
176
177 else:
177 else:
178 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
178 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
179
179
180 datadict = {"time": k,
180 datadict = {"time": k,
181 "commits": 1,
181 "commits": 1,
182 "added": len(cs.added),
182 "added": len(cs.added),
183 "changed": len(cs.changed),
183 "changed": len(cs.changed),
184 "removed": len(cs.removed),
184 "removed": len(cs.removed),
185 }
185 }
186 co_day_auth_aggr[akc(cs.author)]['data']\
186 co_day_auth_aggr[akc(cs.author)]['data']\
187 .append(datadict)
187 .append(datadict)
188
188
189 else:
189 else:
190 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
190 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
191 co_day_auth_aggr[akc(cs.author)] = {
191 co_day_auth_aggr[akc(cs.author)] = {
192 "label": akc(cs.author),
192 "label": akc(cs.author),
193 "data": [{"time":k,
193 "data": [{"time":k,
194 "commits":1,
194 "commits":1,
195 "added":len(cs.added),
195 "added":len(cs.added),
196 "changed":len(cs.changed),
196 "changed":len(cs.changed),
197 "removed":len(cs.removed),
197 "removed":len(cs.removed),
198 }],
198 }],
199 "schema": ["commits"],
199 "schema": ["commits"],
200 }
200 }
201
201
202 #gather all data by day
202 #gather all data by day
203 if k in commits_by_day_aggregate:
203 if k in commits_by_day_aggregate:
204 commits_by_day_aggregate[k] += 1
204 commits_by_day_aggregate[k] += 1
205 else:
205 else:
206 commits_by_day_aggregate[k] = 1
206 commits_by_day_aggregate[k] = 1
207
207
208 overview_data = sorted(commits_by_day_aggregate.items(),
208 overview_data = sorted(commits_by_day_aggregate.items(),
209 key=itemgetter(0))
209 key=itemgetter(0))
210
210
211 if not co_day_auth_aggr:
211 if not co_day_auth_aggr:
212 co_day_auth_aggr[akc(repo.contact)] = {
212 co_day_auth_aggr[akc(repo.contact)] = {
213 "label": akc(repo.contact),
213 "label": akc(repo.contact),
214 "data": [0, 1],
214 "data": [0, 1],
215 "schema": ["commits"],
215 "schema": ["commits"],
216 }
216 }
217
217
218 stats = cur_stats if cur_stats else Statistics()
218 stats = cur_stats if cur_stats else Statistics()
219 stats.commit_activity = json.dumps(co_day_auth_aggr)
219 stats.commit_activity = json.dumps(co_day_auth_aggr)
220 stats.commit_activity_combined = json.dumps(overview_data)
220 stats.commit_activity_combined = json.dumps(overview_data)
221
221
222 log.debug('last revison %s', last_rev)
222 log.debug('last revison %s', last_rev)
223 leftovers = len(repo.revisions[last_rev:])
223 leftovers = len(repo.revisions[last_rev:])
224 log.debug('revisions to parse %s', leftovers)
224 log.debug('revisions to parse %s', leftovers)
225
225
226 if last_rev == 0 or leftovers < parse_limit:
226 if last_rev == 0 or leftovers < parse_limit:
227 log.debug('getting code trending stats')
227 log.debug('getting code trending stats')
228 stats.languages = json.dumps(__get_codes_stats(repo_name))
228 stats.languages = json.dumps(__get_codes_stats(repo_name))
229
229
230 try:
230 try:
231 stats.repository = dbrepo
231 stats.repository = dbrepo
232 stats.stat_on_revision = last_cs.revision if last_cs else 0
232 stats.stat_on_revision = last_cs.revision if last_cs else 0
233 sa.add(stats)
233 sa.add(stats)
234 sa.commit()
234 sa.commit()
235 except:
235 except:
236 log.error(traceback.format_exc())
236 log.error(traceback.format_exc())
237 sa.rollback()
237 sa.rollback()
238 lock.release()
238 lock.release()
239 return False
239 return False
240
240
241 #final release
241 #final release
242 lock.release()
242 lock.release()
243
243
244 #execute another task if celery is enabled
244 #execute another task if celery is enabled
245 if len(repo.revisions) > 1 and CELERY_ON:
245 if len(repo.revisions) > 1 and CELERY_ON:
246 run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
246 run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
247 return True
247 return True
248 except LockHeld:
248 except LockHeld:
249 log.info('LockHeld')
249 log.info('LockHeld')
250 return 'Task with key %s already running' % lockkey
250 return 'Task with key %s already running' % lockkey
251
251
252 @task(ignore_result=True)
253 def send_password_link(user_email):
254 try:
255 log = reset_user_password.get_logger()
256 except:
257 log = logging.getLogger(__name__)
258
259 from rhodecode.lib import auth
260 from rhodecode.model.db import User
261
262 try:
263 sa = get_session()
264 user = sa.query(User).filter(User.email == user_email).scalar()
265
266 if user:
267 link = url('reset_password_confirmation', key=user.api_key,
268 qualified=True)
269 tmpl = """
270 Hello %s
271
272 We received a request to create a new password for your account.
273
274 You can generate it by clicking following URL:
275
276 %s
277
278 If you didn't request new password please ignore this email.
279 """
280 run_task(send_email, user_email,
281 "RhodeCode password reset link",
282 tmpl % (user.short_contact, link))
283 log.info('send new password mail to %s', user_email)
284
285 except:
286 log.error('Failed to update user password')
287 log.error(traceback.format_exc())
288 return False
289
290 return True
252
291
253 @task(ignore_result=True)
292 @task(ignore_result=True)
254 def reset_user_password(user_email):
293 def reset_user_password(user_email):
255 try:
294 try:
256 log = reset_user_password.get_logger()
295 log = reset_user_password.get_logger()
257 except:
296 except:
258 log = logging.getLogger(__name__)
297 log = logging.getLogger(__name__)
259
298
260 from rhodecode.lib import auth
299 from rhodecode.lib import auth
261 from rhodecode.model.db import User
300 from rhodecode.model.db import User
262
301
263 try:
302 try:
264 try:
303 try:
265 sa = get_session()
304 sa = get_session()
266 user = sa.query(User).filter(User.email == user_email).scalar()
305 user = sa.query(User).filter(User.email == user_email).scalar()
267 new_passwd = auth.PasswordGenerator().gen_password(8,
306 new_passwd = auth.PasswordGenerator().gen_password(8,
268 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
307 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
269 if user:
308 if user:
270 user.password = auth.get_crypt_password(new_passwd)
309 user.password = auth.get_crypt_password(new_passwd)
271 user.api_key = auth.generate_api_key(user.username)
310 user.api_key = auth.generate_api_key(user.username)
272 sa.add(user)
311 sa.add(user)
273 sa.commit()
312 sa.commit()
274 log.info('change password for %s', user_email)
313 log.info('change password for %s', user_email)
275 if new_passwd is None:
314 if new_passwd is None:
276 raise Exception('unable to generate new password')
315 raise Exception('unable to generate new password')
277
316
278 except:
317 except:
279 log.error(traceback.format_exc())
318 log.error(traceback.format_exc())
280 sa.rollback()
319 sa.rollback()
281
320
282 run_task(send_email, user_email,
321 run_task(send_email, user_email,
283 "Your new rhodecode password",
322 "Your new RhodeCode password",
284 'Your new rhodecode password:%s' % (new_passwd))
323 'Your new RhodeCode password:%s' % (new_passwd))
285 log.info('send new password mail to %s', user_email)
324 log.info('send new password mail to %s', user_email)
286
325
287 except:
326 except:
288 log.error('Failed to update user password')
327 log.error('Failed to update user password')
289 log.error(traceback.format_exc())
328 log.error(traceback.format_exc())
290
329
291 return True
330 return True
292
331
293
332
294 @task(ignore_result=True)
333 @task(ignore_result=True)
295 def send_email(recipients, subject, body):
334 def send_email(recipients, subject, body):
296 """
335 """
297 Sends an email with defined parameters from the .ini files.
336 Sends an email with defined parameters from the .ini files.
298
337
299 :param recipients: list of recipients, it this is empty the defined email
338 :param recipients: list of recipients, it this is empty the defined email
300 address from field 'email_to' is used instead
339 address from field 'email_to' is used instead
301 :param subject: subject of the mail
340 :param subject: subject of the mail
302 :param body: body of the mail
341 :param body: body of the mail
303 """
342 """
304 try:
343 try:
305 log = send_email.get_logger()
344 log = send_email.get_logger()
306 except:
345 except:
307 log = logging.getLogger(__name__)
346 log = logging.getLogger(__name__)
308
347
309 email_config = config
348 email_config = config
310
349
311 if not recipients:
350 if not recipients:
312 recipients = [email_config.get('email_to')]
351 recipients = [email_config.get('email_to')]
313
352
314 mail_from = email_config.get('app_email_from')
353 mail_from = email_config.get('app_email_from')
315 user = email_config.get('smtp_username')
354 user = email_config.get('smtp_username')
316 passwd = email_config.get('smtp_password')
355 passwd = email_config.get('smtp_password')
317 mail_server = email_config.get('smtp_server')
356 mail_server = email_config.get('smtp_server')
318 mail_port = email_config.get('smtp_port')
357 mail_port = email_config.get('smtp_port')
319 tls = str2bool(email_config.get('smtp_use_tls'))
358 tls = str2bool(email_config.get('smtp_use_tls'))
320 ssl = str2bool(email_config.get('smtp_use_ssl'))
359 ssl = str2bool(email_config.get('smtp_use_ssl'))
321 debug = str2bool(config.get('debug'))
360 debug = str2bool(config.get('debug'))
322
361
323 try:
362 try:
324 m = SmtpMailer(mail_from, user, passwd, mail_server,
363 m = SmtpMailer(mail_from, user, passwd, mail_server,
325 mail_port, ssl, tls, debug=debug)
364 mail_port, ssl, tls, debug=debug)
326 m.send(recipients, subject, body)
365 m.send(recipients, subject, body)
327 except:
366 except:
328 log.error('Mail sending failed')
367 log.error('Mail sending failed')
329 log.error(traceback.format_exc())
368 log.error(traceback.format_exc())
330 return False
369 return False
331 return True
370 return True
332
371
333
372
334 @task(ignore_result=True)
373 @task(ignore_result=True)
335 def create_repo_fork(form_data, cur_user):
374 def create_repo_fork(form_data, cur_user):
336 from rhodecode.model.repo import RepoModel
375 from rhodecode.model.repo import RepoModel
337 from vcs import get_backend
376 from vcs import get_backend
338
377
339 try:
378 try:
340 log = create_repo_fork.get_logger()
379 log = create_repo_fork.get_logger()
341 except:
380 except:
342 log = logging.getLogger(__name__)
381 log = logging.getLogger(__name__)
343
382
344 repo_model = RepoModel(get_session())
383 repo_model = RepoModel(get_session())
345 repo_model.create(form_data, cur_user, just_db=True, fork=True)
384 repo_model.create(form_data, cur_user, just_db=True, fork=True)
346 repo_name = form_data['repo_name']
385 repo_name = form_data['repo_name']
347 repos_path = get_repos_path()
386 repos_path = get_repos_path()
348 repo_path = os.path.join(repos_path, repo_name)
387 repo_path = os.path.join(repos_path, repo_name)
349 repo_fork_path = os.path.join(repos_path, form_data['fork_name'])
388 repo_fork_path = os.path.join(repos_path, form_data['fork_name'])
350 alias = form_data['repo_type']
389 alias = form_data['repo_type']
351
390
352 log.info('creating repo fork %s as %s', repo_name, repo_path)
391 log.info('creating repo fork %s as %s', repo_name, repo_path)
353 backend = get_backend(alias)
392 backend = get_backend(alias)
354 backend(str(repo_fork_path), create=True, src_url=str(repo_path))
393 backend(str(repo_fork_path), create=True, src_url=str(repo_path))
355
394
356
395
357 def __get_codes_stats(repo_name):
396 def __get_codes_stats(repo_name):
358 repos_path = get_repos_path()
397 repos_path = get_repos_path()
359 repo = get_repo(safe_str(os.path.join(repos_path, repo_name)))
398 repo = get_repo(safe_str(os.path.join(repos_path, repo_name)))
360 tip = repo.get_changeset()
399 tip = repo.get_changeset()
361 code_stats = {}
400 code_stats = {}
362
401
363 def aggregate(cs):
402 def aggregate(cs):
364 for f in cs[2]:
403 for f in cs[2]:
365 ext = lower(f.extension)
404 ext = lower(f.extension)
366 if ext in LANGUAGES_EXTENSIONS_MAP.keys() and not f.is_binary:
405 if ext in LANGUAGES_EXTENSIONS_MAP.keys() and not f.is_binary:
367 if ext in code_stats:
406 if ext in code_stats:
368 code_stats[ext] += 1
407 code_stats[ext] += 1
369 else:
408 else:
370 code_stats[ext] = 1
409 code_stats[ext] = 1
371
410
372 map(aggregate, tip.walk('/'))
411 map(aggregate, tip.walk('/'))
373
412
374 return code_stats or {}
413 return code_stats or {}
@@ -1,143 +1,149 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.smtp_mailer
3 rhodecode.lib.smtp_mailer
4 ~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Simple smtp mailer used in RhodeCode
6 Simple smtp mailer used in RhodeCode
7
7
8 :created_on: Sep 13, 2010
8 :created_on: Sep 13, 2010
9 :copyright: (c) 2011 by marcink.
9 :copyright: (c) 2011 by marcink.
10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
11 """
11 """
12
12
13 import logging
13 import logging
14 import smtplib
14 import smtplib
15 import mimetypes
15 import mimetypes
16 from socket import sslerror
16 from socket import sslerror
17
17
18 from email.mime.multipart import MIMEMultipart
18 from email.mime.multipart import MIMEMultipart
19 from email.mime.image import MIMEImage
19 from email.mime.image import MIMEImage
20 from email.mime.audio import MIMEAudio
20 from email.mime.audio import MIMEAudio
21 from email.mime.base import MIMEBase
21 from email.mime.base import MIMEBase
22 from email.mime.text import MIMEText
22 from email.mime.text import MIMEText
23 from email.utils import formatdate
23 from email.utils import formatdate
24 from email import encoders
24 from email import encoders
25
25
26
26
27 class SmtpMailer(object):
27 class SmtpMailer(object):
28 """SMTP mailer class
28 """SMTP mailer class
29
29
30 mailer = SmtpMailer(mail_from, user, passwd, mail_server,
30 mailer = SmtpMailer(mail_from, user, passwd, mail_server,
31 mail_port, ssl, tls)
31 mail_port, ssl, tls)
32 mailer.send(recipients, subject, body, attachment_files)
32 mailer.send(recipients, subject, body, attachment_files)
33
33
34 :param recipients might be a list of string or single string
34 :param recipients might be a list of string or single string
35 :param attachment_files is a dict of {filename:location}
35 :param attachment_files is a dict of {filename:location}
36 it tries to guess the mimetype and attach the file
36 it tries to guess the mimetype and attach the file
37
37
38 """
38 """
39
39
40 def __init__(self, mail_from, user, passwd, mail_server,
40 def __init__(self, mail_from, user, passwd, mail_server,
41 mail_port=None, ssl=False, tls=False, debug=False):
41 mail_port=None, ssl=False, tls=False, debug=False):
42
42
43 self.mail_from = mail_from
43 self.mail_from = mail_from
44 self.mail_server = mail_server
44 self.mail_server = mail_server
45 self.mail_port = mail_port
45 self.mail_port = mail_port
46 self.user = user
46 self.user = user
47 self.passwd = passwd
47 self.passwd = passwd
48 self.ssl = ssl
48 self.ssl = ssl
49 self.tls = tls
49 self.tls = tls
50 self.debug = debug
50 self.debug = debug
51
51
52 def send(self, recipients=[], subject='', body='', attachment_files=None):
52 def send(self, recipients=[], subject='', body='', attachment_files=None):
53
53
54 if isinstance(recipients, basestring):
54 if isinstance(recipients, basestring):
55 recipients = [recipients]
55 recipients = [recipients]
56 if self.ssl:
56 if self.ssl:
57 smtp_serv = smtplib.SMTP_SSL(self.mail_server, self.mail_port)
57 smtp_serv = smtplib.SMTP_SSL(self.mail_server, self.mail_port)
58 else:
58 else:
59 smtp_serv = smtplib.SMTP(self.mail_server, self.mail_port)
59 smtp_serv = smtplib.SMTP(self.mail_server, self.mail_port)
60
60
61 if self.tls:
61 if self.tls:
62 smtp_serv.ehlo()
62 smtp_serv.ehlo()
63 smtp_serv.starttls()
63 smtp_serv.starttls()
64
64
65 if self.debug:
65 if self.debug:
66 smtp_serv.set_debuglevel(1)
66 smtp_serv.set_debuglevel(1)
67
67
68 smtp_serv.ehlo()
68 smtp_serv.ehlo()
69
69
70 #if server requires authorization you must provide login and password
70 #if server requires authorization you must provide login and password
71 #but only if we have them
71 #but only if we have them
72 if self.user and self.passwd:
72 if self.user and self.passwd:
73 smtp_serv.login(self.user, self.passwd)
73 smtp_serv.login(self.user, self.passwd)
74
74
75 date_ = formatdate(localtime=True)
75 date_ = formatdate(localtime=True)
76 msg = MIMEMultipart()
76 msg = MIMEMultipart()
77 msg.set_type('multipart/alternative')
78 msg.preamble = 'You will not see this in a MIME-aware mail reader.\n'
79
80 text_msg = MIMEText(body)
81 text_msg.set_type('text/plain')
82 text_msg.set_param('charset', 'UTF-8')
83
77 msg['From'] = self.mail_from
84 msg['From'] = self.mail_from
78 msg['To'] = ','.join(recipients)
85 msg['To'] = ','.join(recipients)
79 msg['Date'] = date_
86 msg['Date'] = date_
80 msg['Subject'] = subject
87 msg['Subject'] = subject
81 msg.preamble = 'You will not see this in a MIME-aware mail reader.\n'
82
88
83 msg.attach(MIMEText(body))
89 msg.attach(text_msg)
84
90
85 if attachment_files:
91 if attachment_files:
86 self.__atach_files(msg, attachment_files)
92 self.__atach_files(msg, attachment_files)
87
93
88 smtp_serv.sendmail(self.mail_from, recipients, msg.as_string())
94 smtp_serv.sendmail(self.mail_from, recipients, msg.as_string())
89 logging.info('MAIL SEND TO: %s' % recipients)
95 logging.info('MAIL SEND TO: %s' % recipients)
90
96
91 try:
97 try:
92 smtp_serv.quit()
98 smtp_serv.quit()
93 except sslerror:
99 except sslerror:
94 # sslerror is raised in tls connections on closing sometimes
100 # sslerror is raised in tls connections on closing sometimes
95 pass
101 pass
96
102
97 def __atach_files(self, msg, attachment_files):
103 def __atach_files(self, msg, attachment_files):
98 if isinstance(attachment_files, dict):
104 if isinstance(attachment_files, dict):
99 for f_name, msg_file in attachment_files.items():
105 for f_name, msg_file in attachment_files.items():
100 ctype, encoding = mimetypes.guess_type(f_name)
106 ctype, encoding = mimetypes.guess_type(f_name)
101 logging.info("guessing file %s type based on %s", ctype,
107 logging.info("guessing file %s type based on %s", ctype,
102 f_name)
108 f_name)
103 if ctype is None or encoding is not None:
109 if ctype is None or encoding is not None:
104 # No guess could be made, or the file is encoded
110 # No guess could be made, or the file is encoded
105 # (compressed), so use a generic bag-of-bits type.
111 # (compressed), so use a generic bag-of-bits type.
106 ctype = 'application/octet-stream'
112 ctype = 'application/octet-stream'
107 maintype, subtype = ctype.split('/', 1)
113 maintype, subtype = ctype.split('/', 1)
108 if maintype == 'text':
114 if maintype == 'text':
109 # Note: we should handle calculating the charset
115 # Note: we should handle calculating the charset
110 file_part = MIMEText(self.get_content(msg_file),
116 file_part = MIMEText(self.get_content(msg_file),
111 _subtype=subtype)
117 _subtype=subtype)
112 elif maintype == 'image':
118 elif maintype == 'image':
113 file_part = MIMEImage(self.get_content(msg_file),
119 file_part = MIMEImage(self.get_content(msg_file),
114 _subtype=subtype)
120 _subtype=subtype)
115 elif maintype == 'audio':
121 elif maintype == 'audio':
116 file_part = MIMEAudio(self.get_content(msg_file),
122 file_part = MIMEAudio(self.get_content(msg_file),
117 _subtype=subtype)
123 _subtype=subtype)
118 else:
124 else:
119 file_part = MIMEBase(maintype, subtype)
125 file_part = MIMEBase(maintype, subtype)
120 file_part.set_payload(self.get_content(msg_file))
126 file_part.set_payload(self.get_content(msg_file))
121 # Encode the payload using Base64
127 # Encode the payload using Base64
122 encoders.encode_base64(msg)
128 encoders.encode_base64(msg)
123 # Set the filename parameter
129 # Set the filename parameter
124 file_part.add_header('Content-Disposition', 'attachment',
130 file_part.add_header('Content-Disposition', 'attachment',
125 filename=f_name)
131 filename=f_name)
126 file_part.add_header('Content-Type', ctype, name=f_name)
132 file_part.add_header('Content-Type', ctype, name=f_name)
127 msg.attach(file_part)
133 msg.attach(file_part)
128 else:
134 else:
129 raise Exception('Attachment files should be'
135 raise Exception('Attachment files should be'
130 'a dict in format {"filename":"filepath"}')
136 'a dict in format {"filename":"filepath"}')
131
137
132 def get_content(self, msg_file):
138 def get_content(self, msg_file):
133 """Get content based on type, if content is a string do open first
139 """Get content based on type, if content is a string do open first
134 else just read because it's a probably open file object
140 else just read because it's a probably open file object
135
141
136 :param msg_file:
142 :param msg_file:
137 """
143 """
138 if isinstance(msg_file, str):
144 if isinstance(msg_file, str):
139 return open(msg_file, "rb").read()
145 return open(msg_file, "rb").read()
140 else:
146 else:
141 #just for safe seek to 0
147 #just for safe seek to 0
142 msg_file.seek(0)
148 msg_file.seek(0)
143 return msg_file.read()
149 return msg_file.read()
@@ -1,795 +1,800 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.db
3 rhodecode.model.db
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 Database Models for RhodeCode
6 Database Models for RhodeCode
7
7
8 :created_on: Apr 08, 2010
8 :created_on: Apr 08, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import logging
27 import logging
28 import datetime
28 import datetime
29 import traceback
29 import traceback
30 from datetime import date
30 from datetime import date
31
31
32 from sqlalchemy import *
32 from sqlalchemy import *
33 from sqlalchemy.exc import DatabaseError
33 from sqlalchemy.exc import DatabaseError
34 from sqlalchemy.orm import relationship, backref, joinedload, class_mapper
34 from sqlalchemy.orm import relationship, backref, joinedload, class_mapper
35 from sqlalchemy.orm.interfaces import MapperExtension
35 from sqlalchemy.orm.interfaces import MapperExtension
36
36
37 from beaker.cache import cache_region, region_invalidate
37 from beaker.cache import cache_region, region_invalidate
38
38
39 from vcs import get_backend
39 from vcs import get_backend
40 from vcs.utils.helpers import get_scm
40 from vcs.utils.helpers import get_scm
41 from vcs.exceptions import RepositoryError, VCSError
41 from vcs.exceptions import RepositoryError, VCSError
42 from vcs.utils.lazy import LazyProperty
42 from vcs.utils.lazy import LazyProperty
43 from vcs.nodes import FileNode
43 from vcs.nodes import FileNode
44
44
45 from rhodecode.lib import str2bool, json, safe_str
45 from rhodecode.lib import str2bool, json, safe_str
46 from rhodecode.model.meta import Base, Session
46 from rhodecode.model.meta import Base, Session
47 from rhodecode.model.caching_query import FromCache
47 from rhodecode.model.caching_query import FromCache
48
48
49 log = logging.getLogger(__name__)
49 log = logging.getLogger(__name__)
50
50
51 #==============================================================================
51 #==============================================================================
52 # BASE CLASSES
52 # BASE CLASSES
53 #==============================================================================
53 #==============================================================================
54
54
55 class ModelSerializer(json.JSONEncoder):
55 class ModelSerializer(json.JSONEncoder):
56 """
56 """
57 Simple Serializer for JSON,
57 Simple Serializer for JSON,
58
58
59 usage::
59 usage::
60
60
61 to make object customized for serialization implement a __json__
61 to make object customized for serialization implement a __json__
62 method that will return a dict for serialization into json
62 method that will return a dict for serialization into json
63
63
64 example::
64 example::
65
65
66 class Task(object):
66 class Task(object):
67
67
68 def __init__(self, name, value):
68 def __init__(self, name, value):
69 self.name = name
69 self.name = name
70 self.value = value
70 self.value = value
71
71
72 def __json__(self):
72 def __json__(self):
73 return dict(name=self.name,
73 return dict(name=self.name,
74 value=self.value)
74 value=self.value)
75
75
76 """
76 """
77
77
78 def default(self, obj):
78 def default(self, obj):
79
79
80 if hasattr(obj, '__json__'):
80 if hasattr(obj, '__json__'):
81 return obj.__json__()
81 return obj.__json__()
82 else:
82 else:
83 return json.JSONEncoder.default(self, obj)
83 return json.JSONEncoder.default(self, obj)
84
84
85 class BaseModel(object):
85 class BaseModel(object):
86 """Base Model for all classess
86 """Base Model for all classess
87
87
88 """
88 """
89
89
90 @classmethod
90 @classmethod
91 def _get_keys(cls):
91 def _get_keys(cls):
92 """return column names for this model """
92 """return column names for this model """
93 return class_mapper(cls).c.keys()
93 return class_mapper(cls).c.keys()
94
94
95 def get_dict(self):
95 def get_dict(self):
96 """return dict with keys and values corresponding
96 """return dict with keys and values corresponding
97 to this model data """
97 to this model data """
98
98
99 d = {}
99 d = {}
100 for k in self._get_keys():
100 for k in self._get_keys():
101 d[k] = getattr(self, k)
101 d[k] = getattr(self, k)
102 return d
102 return d
103
103
104 def get_appstruct(self):
104 def get_appstruct(self):
105 """return list with keys and values tupples corresponding
105 """return list with keys and values tupples corresponding
106 to this model data """
106 to this model data """
107
107
108 l = []
108 l = []
109 for k in self._get_keys():
109 for k in self._get_keys():
110 l.append((k, getattr(self, k),))
110 l.append((k, getattr(self, k),))
111 return l
111 return l
112
112
113 def populate_obj(self, populate_dict):
113 def populate_obj(self, populate_dict):
114 """populate model with data from given populate_dict"""
114 """populate model with data from given populate_dict"""
115
115
116 for k in self._get_keys():
116 for k in self._get_keys():
117 if k in populate_dict:
117 if k in populate_dict:
118 setattr(self, k, populate_dict[k])
118 setattr(self, k, populate_dict[k])
119
119
120 @classmethod
120 @classmethod
121 def query(cls):
121 def query(cls):
122 return Session.query(cls)
122 return Session.query(cls)
123
123
124 @classmethod
124 @classmethod
125 def get(cls, id_):
125 def get(cls, id_):
126 return Session.query(cls).get(id_)
126 return Session.query(cls).get(id_)
127
127
128
128
129 class RhodeCodeSettings(Base, BaseModel):
129 class RhodeCodeSettings(Base, BaseModel):
130 __tablename__ = 'rhodecode_settings'
130 __tablename__ = 'rhodecode_settings'
131 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
131 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
132 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
132 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
133 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
133 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
134 app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
134 app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
135
135
136 def __init__(self, k='', v=''):
136 def __init__(self, k='', v=''):
137 self.app_settings_name = k
137 self.app_settings_name = k
138 self.app_settings_value = v
138 self.app_settings_value = v
139
139
140 def __repr__(self):
140 def __repr__(self):
141 return "<%s('%s:%s')>" % (self.__class__.__name__,
141 return "<%s('%s:%s')>" % (self.__class__.__name__,
142 self.app_settings_name, self.app_settings_value)
142 self.app_settings_name, self.app_settings_value)
143
143
144
144
145 @classmethod
145 @classmethod
146 def get_by_name(cls, ldap_key):
146 def get_by_name(cls, ldap_key):
147 return Session.query(cls)\
147 return Session.query(cls)\
148 .filter(cls.app_settings_name == ldap_key).scalar()
148 .filter(cls.app_settings_name == ldap_key).scalar()
149
149
150 @classmethod
150 @classmethod
151 def get_app_settings(cls, cache=False):
151 def get_app_settings(cls, cache=False):
152
152
153 ret = Session.query(cls)
153 ret = Session.query(cls)
154
154
155 if cache:
155 if cache:
156 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
156 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
157
157
158 if not ret:
158 if not ret:
159 raise Exception('Could not get application settings !')
159 raise Exception('Could not get application settings !')
160 settings = {}
160 settings = {}
161 for each in ret:
161 for each in ret:
162 settings['rhodecode_' + each.app_settings_name] = \
162 settings['rhodecode_' + each.app_settings_name] = \
163 each.app_settings_value
163 each.app_settings_value
164
164
165 return settings
165 return settings
166
166
167 @classmethod
167 @classmethod
168 def get_ldap_settings(cls, cache=False):
168 def get_ldap_settings(cls, cache=False):
169 ret = Session.query(cls)\
169 ret = Session.query(cls)\
170 .filter(cls.app_settings_name.startswith('ldap_'))\
170 .filter(cls.app_settings_name.startswith('ldap_'))\
171 .all()
171 .all()
172 fd = {}
172 fd = {}
173 for row in ret:
173 for row in ret:
174 fd.update({row.app_settings_name:row.app_settings_value})
174 fd.update({row.app_settings_name:row.app_settings_value})
175
175
176 fd.update({'ldap_active':str2bool(fd.get('ldap_active'))})
176 fd.update({'ldap_active':str2bool(fd.get('ldap_active'))})
177
177
178 return fd
178 return fd
179
179
180
180
181 class RhodeCodeUi(Base, BaseModel):
181 class RhodeCodeUi(Base, BaseModel):
182 __tablename__ = 'rhodecode_ui'
182 __tablename__ = 'rhodecode_ui'
183 __table_args__ = {'extend_existing':True}
183 __table_args__ = {'extend_existing':True}
184 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
184 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
185 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
185 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
186 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
186 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
187 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
187 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
188 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
188 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
189
189
190
190
191 @classmethod
191 @classmethod
192 def get_by_key(cls, key):
192 def get_by_key(cls, key):
193 return Session.query(cls).filter(cls.ui_key == key)
193 return Session.query(cls).filter(cls.ui_key == key)
194
194
195
195
196 class User(Base, BaseModel):
196 class User(Base, BaseModel):
197 __tablename__ = 'users'
197 __tablename__ = 'users'
198 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
198 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
199 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
199 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
200 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
200 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
201 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
201 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
202 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
202 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
203 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
203 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
204 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
204 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
205 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
205 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
206 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
206 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
207 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
207 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
208 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
208 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
209 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
209 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
210
210
211 user_log = relationship('UserLog', cascade='all')
211 user_log = relationship('UserLog', cascade='all')
212 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
212 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
213
213
214 repositories = relationship('Repository')
214 repositories = relationship('Repository')
215 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
215 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
216 repo_to_perm = relationship('RepoToPerm', primaryjoin='RepoToPerm.user_id==User.user_id', cascade='all')
216 repo_to_perm = relationship('RepoToPerm', primaryjoin='RepoToPerm.user_id==User.user_id', cascade='all')
217
217
218 group_member = relationship('UsersGroupMember', cascade='all')
218 group_member = relationship('UsersGroupMember', cascade='all')
219
219
220 @property
220 @property
221 def full_contact(self):
221 def full_contact(self):
222 return '%s %s <%s>' % (self.name, self.lastname, self.email)
222 return '%s %s <%s>' % (self.name, self.lastname, self.email)
223
223
224 @property
224 @property
225 def short_contact(self):
225 def short_contact(self):
226 return '%s %s' % (self.name, self.lastname)
226 return '%s %s' % (self.name, self.lastname)
227
227
228 @property
228 @property
229 def is_admin(self):
229 def is_admin(self):
230 return self.admin
230 return self.admin
231
231
232 def __repr__(self):
232 def __repr__(self):
233 try:
233 try:
234 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
234 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
235 self.user_id, self.username)
235 self.user_id, self.username)
236 except:
236 except:
237 return self.__class__.__name__
237 return self.__class__.__name__
238
238
239 @classmethod
239 @classmethod
240 def by_username(cls, username, case_insensitive=False):
240 def by_username(cls, username, case_insensitive=False):
241 if case_insensitive:
241 if case_insensitive:
242 return Session.query(cls).filter(cls.username.like(username)).one()
242 return Session.query(cls).filter(cls.username.like(username)).one()
243 else:
243 else:
244 return Session.query(cls).filter(cls.username == username).one()
244 return Session.query(cls).filter(cls.username == username).one()
245
245
246 @classmethod
247 def get_by_api_key(cls, api_key):
248 return Session.query(cls).filter(cls.api_key == api_key).one()
249
250
246 def update_lastlogin(self):
251 def update_lastlogin(self):
247 """Update user lastlogin"""
252 """Update user lastlogin"""
248
253
249 self.last_login = datetime.datetime.now()
254 self.last_login = datetime.datetime.now()
250 Session.add(self)
255 Session.add(self)
251 Session.commit()
256 Session.commit()
252 log.debug('updated user %s lastlogin', self.username)
257 log.debug('updated user %s lastlogin', self.username)
253
258
254
259
255 class UserLog(Base, BaseModel):
260 class UserLog(Base, BaseModel):
256 __tablename__ = 'user_logs'
261 __tablename__ = 'user_logs'
257 __table_args__ = {'extend_existing':True}
262 __table_args__ = {'extend_existing':True}
258 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
263 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
259 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
264 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
260 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
265 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
261 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
266 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
262 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
267 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
263 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
268 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
264 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
269 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
265
270
266 @property
271 @property
267 def action_as_day(self):
272 def action_as_day(self):
268 return date(*self.action_date.timetuple()[:3])
273 return date(*self.action_date.timetuple()[:3])
269
274
270 user = relationship('User')
275 user = relationship('User')
271 repository = relationship('Repository')
276 repository = relationship('Repository')
272
277
273
278
274 class UsersGroup(Base, BaseModel):
279 class UsersGroup(Base, BaseModel):
275 __tablename__ = 'users_groups'
280 __tablename__ = 'users_groups'
276 __table_args__ = {'extend_existing':True}
281 __table_args__ = {'extend_existing':True}
277
282
278 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
283 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
279 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
284 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
280 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
285 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
281
286
282 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
287 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
283
288
284
289
285 @classmethod
290 @classmethod
286 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
291 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
287 if case_insensitive:
292 if case_insensitive:
288 gr = Session.query(cls)\
293 gr = Session.query(cls)\
289 .filter(cls.users_group_name.ilike(group_name))
294 .filter(cls.users_group_name.ilike(group_name))
290 else:
295 else:
291 gr = Session.query(UsersGroup)\
296 gr = Session.query(UsersGroup)\
292 .filter(UsersGroup.users_group_name == group_name)
297 .filter(UsersGroup.users_group_name == group_name)
293 if cache:
298 if cache:
294 gr = gr.options(FromCache("sql_cache_short",
299 gr = gr.options(FromCache("sql_cache_short",
295 "get_user_%s" % group_name))
300 "get_user_%s" % group_name))
296 return gr.scalar()
301 return gr.scalar()
297
302
298 class UsersGroupMember(Base, BaseModel):
303 class UsersGroupMember(Base, BaseModel):
299 __tablename__ = 'users_groups_members'
304 __tablename__ = 'users_groups_members'
300 __table_args__ = {'extend_existing':True}
305 __table_args__ = {'extend_existing':True}
301
306
302 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
307 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
303 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
308 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
304 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
309 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
305
310
306 user = relationship('User', lazy='joined')
311 user = relationship('User', lazy='joined')
307 users_group = relationship('UsersGroup')
312 users_group = relationship('UsersGroup')
308
313
309 def __init__(self, gr_id='', u_id=''):
314 def __init__(self, gr_id='', u_id=''):
310 self.users_group_id = gr_id
315 self.users_group_id = gr_id
311 self.user_id = u_id
316 self.user_id = u_id
312
317
313 class Repository(Base, BaseModel):
318 class Repository(Base, BaseModel):
314 __tablename__ = 'repositories'
319 __tablename__ = 'repositories'
315 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
320 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
316
321
317 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
322 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
318 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
323 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
319 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
324 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
320 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
325 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
321 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
326 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
322 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
327 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
323 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
328 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
324 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
329 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
325 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
330 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
326 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
331 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
327
332
328 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
333 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
329 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
334 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
330
335
331
336
332 user = relationship('User')
337 user = relationship('User')
333 fork = relationship('Repository', remote_side=repo_id)
338 fork = relationship('Repository', remote_side=repo_id)
334 group = relationship('Group')
339 group = relationship('Group')
335 repo_to_perm = relationship('RepoToPerm', cascade='all', order_by='RepoToPerm.repo_to_perm_id')
340 repo_to_perm = relationship('RepoToPerm', cascade='all', order_by='RepoToPerm.repo_to_perm_id')
336 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
341 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
337 stats = relationship('Statistics', cascade='all', uselist=False)
342 stats = relationship('Statistics', cascade='all', uselist=False)
338
343
339 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
344 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
340
345
341 logs = relationship('UserLog', cascade='all')
346 logs = relationship('UserLog', cascade='all')
342
347
343 def __repr__(self):
348 def __repr__(self):
344 return "<%s('%s:%s')>" % (self.__class__.__name__,
349 return "<%s('%s:%s')>" % (self.__class__.__name__,
345 self.repo_id, self.repo_name)
350 self.repo_id, self.repo_name)
346
351
347 @classmethod
352 @classmethod
348 def by_repo_name(cls, repo_name):
353 def by_repo_name(cls, repo_name):
349 q = Session.query(cls).filter(cls.repo_name == repo_name)
354 q = Session.query(cls).filter(cls.repo_name == repo_name)
350
355
351 q = q.options(joinedload(Repository.fork))\
356 q = q.options(joinedload(Repository.fork))\
352 .options(joinedload(Repository.user))\
357 .options(joinedload(Repository.user))\
353 .options(joinedload(Repository.group))\
358 .options(joinedload(Repository.group))\
354
359
355 return q.one()
360 return q.one()
356
361
357 @classmethod
362 @classmethod
358 def get_repo_forks(cls, repo_id):
363 def get_repo_forks(cls, repo_id):
359 return Session.query(cls).filter(Repository.fork_id == repo_id)
364 return Session.query(cls).filter(Repository.fork_id == repo_id)
360
365
361 @property
366 @property
362 def just_name(self):
367 def just_name(self):
363 return self.repo_name.split(os.sep)[-1]
368 return self.repo_name.split(os.sep)[-1]
364
369
365 @property
370 @property
366 def groups_with_parents(self):
371 def groups_with_parents(self):
367 groups = []
372 groups = []
368 if self.group is None:
373 if self.group is None:
369 return groups
374 return groups
370
375
371 cur_gr = self.group
376 cur_gr = self.group
372 groups.insert(0, cur_gr)
377 groups.insert(0, cur_gr)
373 while 1:
378 while 1:
374 gr = getattr(cur_gr, 'parent_group', None)
379 gr = getattr(cur_gr, 'parent_group', None)
375 cur_gr = cur_gr.parent_group
380 cur_gr = cur_gr.parent_group
376 if gr is None:
381 if gr is None:
377 break
382 break
378 groups.insert(0, gr)
383 groups.insert(0, gr)
379
384
380 return groups
385 return groups
381
386
382 @property
387 @property
383 def groups_and_repo(self):
388 def groups_and_repo(self):
384 return self.groups_with_parents, self.just_name
389 return self.groups_with_parents, self.just_name
385
390
386 @LazyProperty
391 @LazyProperty
387 def repo_path(self):
392 def repo_path(self):
388 """
393 """
389 Returns base full path for that repository means where it actually
394 Returns base full path for that repository means where it actually
390 exists on a filesystem
395 exists on a filesystem
391 """
396 """
392 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/')
397 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/')
393 q.options(FromCache("sql_cache_short", "repository_repo_path"))
398 q.options(FromCache("sql_cache_short", "repository_repo_path"))
394 return q.one().ui_value
399 return q.one().ui_value
395
400
396 @property
401 @property
397 def repo_full_path(self):
402 def repo_full_path(self):
398 p = [self.repo_path]
403 p = [self.repo_path]
399 # we need to split the name by / since this is how we store the
404 # we need to split the name by / since this is how we store the
400 # names in the database, but that eventually needs to be converted
405 # names in the database, but that eventually needs to be converted
401 # into a valid system path
406 # into a valid system path
402 p += self.repo_name.split('/')
407 p += self.repo_name.split('/')
403 return os.path.join(*p)
408 return os.path.join(*p)
404
409
405 @property
410 @property
406 def _ui(self):
411 def _ui(self):
407 """
412 """
408 Creates an db based ui object for this repository
413 Creates an db based ui object for this repository
409 """
414 """
410 from mercurial import ui
415 from mercurial import ui
411 from mercurial import config
416 from mercurial import config
412 baseui = ui.ui()
417 baseui = ui.ui()
413
418
414 #clean the baseui object
419 #clean the baseui object
415 baseui._ocfg = config.config()
420 baseui._ocfg = config.config()
416 baseui._ucfg = config.config()
421 baseui._ucfg = config.config()
417 baseui._tcfg = config.config()
422 baseui._tcfg = config.config()
418
423
419
424
420 ret = Session.query(RhodeCodeUi)\
425 ret = Session.query(RhodeCodeUi)\
421 .options(FromCache("sql_cache_short",
426 .options(FromCache("sql_cache_short",
422 "repository_repo_ui")).all()
427 "repository_repo_ui")).all()
423
428
424 hg_ui = ret
429 hg_ui = ret
425 for ui_ in hg_ui:
430 for ui_ in hg_ui:
426 if ui_.ui_active:
431 if ui_.ui_active:
427 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
432 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
428 ui_.ui_key, ui_.ui_value)
433 ui_.ui_key, ui_.ui_value)
429 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
434 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
430
435
431 return baseui
436 return baseui
432
437
433 #==========================================================================
438 #==========================================================================
434 # SCM CACHE INSTANCE
439 # SCM CACHE INSTANCE
435 #==========================================================================
440 #==========================================================================
436
441
437 @property
442 @property
438 def invalidate(self):
443 def invalidate(self):
439 """
444 """
440 Returns Invalidation object if this repo should be invalidated
445 Returns Invalidation object if this repo should be invalidated
441 None otherwise. `cache_active = False` means that this cache
446 None otherwise. `cache_active = False` means that this cache
442 state is not valid and needs to be invalidated
447 state is not valid and needs to be invalidated
443 """
448 """
444 return Session.query(CacheInvalidation)\
449 return Session.query(CacheInvalidation)\
445 .filter(CacheInvalidation.cache_key == self.repo_name)\
450 .filter(CacheInvalidation.cache_key == self.repo_name)\
446 .filter(CacheInvalidation.cache_active == False)\
451 .filter(CacheInvalidation.cache_active == False)\
447 .scalar()
452 .scalar()
448
453
449 @property
454 @property
450 def set_invalidate(self):
455 def set_invalidate(self):
451 """
456 """
452 set a cache for invalidation for this instance
457 set a cache for invalidation for this instance
453 """
458 """
454 inv = Session.query(CacheInvalidation)\
459 inv = Session.query(CacheInvalidation)\
455 .filter(CacheInvalidation.cache_key == self.repo_name)\
460 .filter(CacheInvalidation.cache_key == self.repo_name)\
456 .scalar()
461 .scalar()
457
462
458 if inv is None:
463 if inv is None:
459 inv = CacheInvalidation(self.repo_name)
464 inv = CacheInvalidation(self.repo_name)
460 inv.cache_active = True
465 inv.cache_active = True
461 Session.add(inv)
466 Session.add(inv)
462 Session.commit()
467 Session.commit()
463
468
464 @property
469 @property
465 def scm_instance(self):
470 def scm_instance(self):
466 return self.__get_instance()
471 return self.__get_instance()
467
472
468 @property
473 @property
469 def scm_instance_cached(self):
474 def scm_instance_cached(self):
470 @cache_region('long_term')
475 @cache_region('long_term')
471 def _c(repo_name):
476 def _c(repo_name):
472 return self.__get_instance()
477 return self.__get_instance()
473
478
474 inv = self.invalidate
479 inv = self.invalidate
475 if inv:
480 if inv:
476 region_invalidate(_c, None, self.repo_name)
481 region_invalidate(_c, None, self.repo_name)
477 #update our cache
482 #update our cache
478 inv.cache_key.cache_active = True
483 inv.cache_key.cache_active = True
479 Session.add(inv)
484 Session.add(inv)
480 Session.commit()
485 Session.commit()
481
486
482 # TODO: remove this trick when beaker 1.6 is released
487 # TODO: remove this trick when beaker 1.6 is released
483 # and have fixed this issue
488 # and have fixed this issue
484 rn = safe_str(self.repo_name)
489 rn = safe_str(self.repo_name)
485
490
486 return _c(rn)
491 return _c(rn)
487
492
488 def __get_instance(self):
493 def __get_instance(self):
489
494
490 repo_full_path = self.repo_full_path
495 repo_full_path = self.repo_full_path
491
496
492 try:
497 try:
493 alias = get_scm(repo_full_path)[0]
498 alias = get_scm(repo_full_path)[0]
494 log.debug('Creating instance of %s repository', alias)
499 log.debug('Creating instance of %s repository', alias)
495 backend = get_backend(alias)
500 backend = get_backend(alias)
496 except VCSError:
501 except VCSError:
497 log.error(traceback.format_exc())
502 log.error(traceback.format_exc())
498 log.error('Perhaps this repository is in db and not in '
503 log.error('Perhaps this repository is in db and not in '
499 'filesystem run rescan repositories with '
504 'filesystem run rescan repositories with '
500 '"destroy old data " option from admin panel')
505 '"destroy old data " option from admin panel')
501 return
506 return
502
507
503 if alias == 'hg':
508 if alias == 'hg':
504
509
505 repo = backend(safe_str(repo_full_path), create=False,
510 repo = backend(safe_str(repo_full_path), create=False,
506 baseui=self._ui)
511 baseui=self._ui)
507 #skip hidden web repository
512 #skip hidden web repository
508 if repo._get_hidden():
513 if repo._get_hidden():
509 return
514 return
510 else:
515 else:
511 repo = backend(repo_full_path, create=False)
516 repo = backend(repo_full_path, create=False)
512
517
513 return repo
518 return repo
514
519
515
520
516 class Group(Base, BaseModel):
521 class Group(Base, BaseModel):
517 __tablename__ = 'groups'
522 __tablename__ = 'groups'
518 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
523 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
519 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
524 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
520 __mapper_args__ = {'order_by':'group_name'}
525 __mapper_args__ = {'order_by':'group_name'}
521
526
522 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
527 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
523 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
528 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
524 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
529 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
525 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
530 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
526
531
527 parent_group = relationship('Group', remote_side=group_id)
532 parent_group = relationship('Group', remote_side=group_id)
528
533
529
534
530 def __init__(self, group_name='', parent_group=None):
535 def __init__(self, group_name='', parent_group=None):
531 self.group_name = group_name
536 self.group_name = group_name
532 self.parent_group = parent_group
537 self.parent_group = parent_group
533
538
534 def __repr__(self):
539 def __repr__(self):
535 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
540 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
536 self.group_name)
541 self.group_name)
537
542
538 @classmethod
543 @classmethod
539 def url_sep(cls):
544 def url_sep(cls):
540 return '/'
545 return '/'
541
546
542 @property
547 @property
543 def parents(self):
548 def parents(self):
544 parents_recursion_limit = 5
549 parents_recursion_limit = 5
545 groups = []
550 groups = []
546 if self.parent_group is None:
551 if self.parent_group is None:
547 return groups
552 return groups
548 cur_gr = self.parent_group
553 cur_gr = self.parent_group
549 groups.insert(0, cur_gr)
554 groups.insert(0, cur_gr)
550 cnt = 0
555 cnt = 0
551 while 1:
556 while 1:
552 cnt += 1
557 cnt += 1
553 gr = getattr(cur_gr, 'parent_group', None)
558 gr = getattr(cur_gr, 'parent_group', None)
554 cur_gr = cur_gr.parent_group
559 cur_gr = cur_gr.parent_group
555 if gr is None:
560 if gr is None:
556 break
561 break
557 if cnt == parents_recursion_limit:
562 if cnt == parents_recursion_limit:
558 # this will prevent accidental infinit loops
563 # this will prevent accidental infinit loops
559 log.error('group nested more than %s' %
564 log.error('group nested more than %s' %
560 parents_recursion_limit)
565 parents_recursion_limit)
561 break
566 break
562
567
563 groups.insert(0, gr)
568 groups.insert(0, gr)
564 return groups
569 return groups
565
570
566 @property
571 @property
567 def children(self):
572 def children(self):
568 return Session.query(Group).filter(Group.parent_group == self)
573 return Session.query(Group).filter(Group.parent_group == self)
569
574
570 @property
575 @property
571 def full_path(self):
576 def full_path(self):
572 return Group.url_sep().join([g.group_name for g in self.parents] +
577 return Group.url_sep().join([g.group_name for g in self.parents] +
573 [self.group_name])
578 [self.group_name])
574
579
575 @property
580 @property
576 def repositories(self):
581 def repositories(self):
577 return Session.query(Repository).filter(Repository.group == self)
582 return Session.query(Repository).filter(Repository.group == self)
578
583
579 @property
584 @property
580 def repositories_recursive_count(self):
585 def repositories_recursive_count(self):
581 cnt = self.repositories.count()
586 cnt = self.repositories.count()
582
587
583 def children_count(group):
588 def children_count(group):
584 cnt = 0
589 cnt = 0
585 for child in group.children:
590 for child in group.children:
586 cnt += child.repositories.count()
591 cnt += child.repositories.count()
587 cnt += children_count(child)
592 cnt += children_count(child)
588 return cnt
593 return cnt
589
594
590 return cnt + children_count(self)
595 return cnt + children_count(self)
591
596
592 class Permission(Base, BaseModel):
597 class Permission(Base, BaseModel):
593 __tablename__ = 'permissions'
598 __tablename__ = 'permissions'
594 __table_args__ = {'extend_existing':True}
599 __table_args__ = {'extend_existing':True}
595 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
600 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
596 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
601 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
597 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
602 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
598
603
599 def __repr__(self):
604 def __repr__(self):
600 return "<%s('%s:%s')>" % (self.__class__.__name__,
605 return "<%s('%s:%s')>" % (self.__class__.__name__,
601 self.permission_id, self.permission_name)
606 self.permission_id, self.permission_name)
602
607
603 @classmethod
608 @classmethod
604 def get_by_key(cls, key):
609 def get_by_key(cls, key):
605 return Session.query(cls).filter(cls.permission_name == key).scalar()
610 return Session.query(cls).filter(cls.permission_name == key).scalar()
606
611
607 class RepoToPerm(Base, BaseModel):
612 class RepoToPerm(Base, BaseModel):
608 __tablename__ = 'repo_to_perm'
613 __tablename__ = 'repo_to_perm'
609 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True})
614 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True})
610 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
615 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
611 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
616 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
612 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
617 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
613 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
618 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
614
619
615 user = relationship('User')
620 user = relationship('User')
616 permission = relationship('Permission')
621 permission = relationship('Permission')
617 repository = relationship('Repository')
622 repository = relationship('Repository')
618
623
619 class UserToPerm(Base, BaseModel):
624 class UserToPerm(Base, BaseModel):
620 __tablename__ = 'user_to_perm'
625 __tablename__ = 'user_to_perm'
621 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
626 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
622 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
627 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
623 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
628 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
624 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
629 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
625
630
626 user = relationship('User')
631 user = relationship('User')
627 permission = relationship('Permission')
632 permission = relationship('Permission')
628
633
629 @classmethod
634 @classmethod
630 def has_perm(cls, user_id, perm):
635 def has_perm(cls, user_id, perm):
631 if not isinstance(perm, Permission):
636 if not isinstance(perm, Permission):
632 raise Exception('perm needs to be an instance of Permission class')
637 raise Exception('perm needs to be an instance of Permission class')
633
638
634 return Session.query(cls).filter(cls.user_id == user_id)\
639 return Session.query(cls).filter(cls.user_id == user_id)\
635 .filter(cls.permission == perm).scalar() is not None
640 .filter(cls.permission == perm).scalar() is not None
636
641
637 @classmethod
642 @classmethod
638 def grant_perm(cls, user_id, perm):
643 def grant_perm(cls, user_id, perm):
639 if not isinstance(perm, Permission):
644 if not isinstance(perm, Permission):
640 raise Exception('perm needs to be an instance of Permission class')
645 raise Exception('perm needs to be an instance of Permission class')
641
646
642 new = cls()
647 new = cls()
643 new.user_id = user_id
648 new.user_id = user_id
644 new.permission = perm
649 new.permission = perm
645 try:
650 try:
646 Session.add(new)
651 Session.add(new)
647 Session.commit()
652 Session.commit()
648 except:
653 except:
649 Session.rollback()
654 Session.rollback()
650
655
651
656
652 @classmethod
657 @classmethod
653 def revoke_perm(cls, user_id, perm):
658 def revoke_perm(cls, user_id, perm):
654 if not isinstance(perm, Permission):
659 if not isinstance(perm, Permission):
655 raise Exception('perm needs to be an instance of Permission class')
660 raise Exception('perm needs to be an instance of Permission class')
656
661
657 try:
662 try:
658 Session.query(cls).filter(cls.user_id == user_id)\
663 Session.query(cls).filter(cls.user_id == user_id)\
659 .filter(cls.permission == perm).delete()
664 .filter(cls.permission == perm).delete()
660 Session.commit()
665 Session.commit()
661 except:
666 except:
662 Session.rollback()
667 Session.rollback()
663
668
664 class UsersGroupRepoToPerm(Base, BaseModel):
669 class UsersGroupRepoToPerm(Base, BaseModel):
665 __tablename__ = 'users_group_repo_to_perm'
670 __tablename__ = 'users_group_repo_to_perm'
666 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True})
671 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True})
667 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
672 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
668 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
673 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
669 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
674 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
670 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
675 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
671
676
672 users_group = relationship('UsersGroup')
677 users_group = relationship('UsersGroup')
673 permission = relationship('Permission')
678 permission = relationship('Permission')
674 repository = relationship('Repository')
679 repository = relationship('Repository')
675
680
676
681
677 class UsersGroupToPerm(Base, BaseModel):
682 class UsersGroupToPerm(Base, BaseModel):
678 __tablename__ = 'users_group_to_perm'
683 __tablename__ = 'users_group_to_perm'
679 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
684 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
680 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
685 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
681 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
686 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
682
687
683 users_group = relationship('UsersGroup')
688 users_group = relationship('UsersGroup')
684 permission = relationship('Permission')
689 permission = relationship('Permission')
685
690
686
691
687 @classmethod
692 @classmethod
688 def has_perm(cls, users_group_id, perm):
693 def has_perm(cls, users_group_id, perm):
689 if not isinstance(perm, Permission):
694 if not isinstance(perm, Permission):
690 raise Exception('perm needs to be an instance of Permission class')
695 raise Exception('perm needs to be an instance of Permission class')
691
696
692 return Session.query(cls).filter(cls.users_group_id ==
697 return Session.query(cls).filter(cls.users_group_id ==
693 users_group_id)\
698 users_group_id)\
694 .filter(cls.permission == perm)\
699 .filter(cls.permission == perm)\
695 .scalar() is not None
700 .scalar() is not None
696
701
697 @classmethod
702 @classmethod
698 def grant_perm(cls, users_group_id, perm):
703 def grant_perm(cls, users_group_id, perm):
699 if not isinstance(perm, Permission):
704 if not isinstance(perm, Permission):
700 raise Exception('perm needs to be an instance of Permission class')
705 raise Exception('perm needs to be an instance of Permission class')
701
706
702 new = cls()
707 new = cls()
703 new.users_group_id = users_group_id
708 new.users_group_id = users_group_id
704 new.permission = perm
709 new.permission = perm
705 try:
710 try:
706 Session.add(new)
711 Session.add(new)
707 Session.commit()
712 Session.commit()
708 except:
713 except:
709 Session.rollback()
714 Session.rollback()
710
715
711
716
712 @classmethod
717 @classmethod
713 def revoke_perm(cls, users_group_id, perm):
718 def revoke_perm(cls, users_group_id, perm):
714 if not isinstance(perm, Permission):
719 if not isinstance(perm, Permission):
715 raise Exception('perm needs to be an instance of Permission class')
720 raise Exception('perm needs to be an instance of Permission class')
716
721
717 try:
722 try:
718 Session.query(cls).filter(cls.users_group_id == users_group_id)\
723 Session.query(cls).filter(cls.users_group_id == users_group_id)\
719 .filter(cls.permission == perm).delete()
724 .filter(cls.permission == perm).delete()
720 Session.commit()
725 Session.commit()
721 except:
726 except:
722 Session.rollback()
727 Session.rollback()
723
728
724
729
725 class GroupToPerm(Base, BaseModel):
730 class GroupToPerm(Base, BaseModel):
726 __tablename__ = 'group_to_perm'
731 __tablename__ = 'group_to_perm'
727 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
732 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
728
733
729 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
734 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
730 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
735 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
731 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
736 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
732 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
737 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
733
738
734 user = relationship('User')
739 user = relationship('User')
735 permission = relationship('Permission')
740 permission = relationship('Permission')
736 group = relationship('Group')
741 group = relationship('Group')
737
742
738 class Statistics(Base, BaseModel):
743 class Statistics(Base, BaseModel):
739 __tablename__ = 'statistics'
744 __tablename__ = 'statistics'
740 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True})
745 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True})
741 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
746 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
742 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
747 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
743 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
748 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
744 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
749 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
745 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
750 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
746 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
751 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
747
752
748 repository = relationship('Repository', single_parent=True)
753 repository = relationship('Repository', single_parent=True)
749
754
750 class UserFollowing(Base, BaseModel):
755 class UserFollowing(Base, BaseModel):
751 __tablename__ = 'user_followings'
756 __tablename__ = 'user_followings'
752 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
757 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
753 UniqueConstraint('user_id', 'follows_user_id')
758 UniqueConstraint('user_id', 'follows_user_id')
754 , {'extend_existing':True})
759 , {'extend_existing':True})
755
760
756 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
761 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
757 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
762 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
758 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
763 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
759 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
764 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
760 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
765 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
761
766
762 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
767 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
763
768
764 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
769 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
765 follows_repository = relationship('Repository', order_by='Repository.repo_name')
770 follows_repository = relationship('Repository', order_by='Repository.repo_name')
766
771
767
772
768 @classmethod
773 @classmethod
769 def get_repo_followers(cls, repo_id):
774 def get_repo_followers(cls, repo_id):
770 return Session.query(cls).filter(cls.follows_repo_id == repo_id)
775 return Session.query(cls).filter(cls.follows_repo_id == repo_id)
771
776
772 class CacheInvalidation(Base, BaseModel):
777 class CacheInvalidation(Base, BaseModel):
773 __tablename__ = 'cache_invalidation'
778 __tablename__ = 'cache_invalidation'
774 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True})
779 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True})
775 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
780 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
776 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
781 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
777 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
782 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
778 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
783 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
779
784
780
785
781 def __init__(self, cache_key, cache_args=''):
786 def __init__(self, cache_key, cache_args=''):
782 self.cache_key = cache_key
787 self.cache_key = cache_key
783 self.cache_args = cache_args
788 self.cache_args = cache_args
784 self.cache_active = False
789 self.cache_active = False
785
790
786 def __repr__(self):
791 def __repr__(self):
787 return "<%s('%s:%s')>" % (self.__class__.__name__,
792 return "<%s('%s:%s')>" % (self.__class__.__name__,
788 self.cache_id, self.cache_key)
793 self.cache_id, self.cache_key)
789
794
790 class DbMigrateVersion(Base, BaseModel):
795 class DbMigrateVersion(Base, BaseModel):
791 __tablename__ = 'db_migrate_version'
796 __tablename__ = 'db_migrate_version'
792 __table_args__ = {'extend_existing':True}
797 __table_args__ = {'extend_existing':True}
793 repository_id = Column('repository_id', String(250), primary_key=True)
798 repository_id = Column('repository_id', String(250), primary_key=True)
794 repository_path = Column('repository_path', Text)
799 repository_path = Column('repository_path', Text)
795 version = Column('version', Integer)
800 version = Column('version', Integer)
@@ -1,383 +1,387 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.user
3 rhodecode.model.user
4 ~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~
5
5
6 users model for RhodeCode
6 users model for RhodeCode
7
7
8 :created_on: Apr 9, 2010
8 :created_on: Apr 9, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28
28
29 from pylons.i18n.translation import _
29 from pylons.i18n.translation import _
30
30
31 from rhodecode.model import BaseModel
31 from rhodecode.model import BaseModel
32 from rhodecode.model.caching_query import FromCache
32 from rhodecode.model.caching_query import FromCache
33 from rhodecode.model.db import User, RepoToPerm, Repository, Permission, \
33 from rhodecode.model.db import User, RepoToPerm, Repository, Permission, \
34 UserToPerm, UsersGroupRepoToPerm, UsersGroupToPerm, UsersGroupMember
34 UserToPerm, UsersGroupRepoToPerm, UsersGroupToPerm, UsersGroupMember
35 from rhodecode.lib.exceptions import DefaultUserException, \
35 from rhodecode.lib.exceptions import DefaultUserException, \
36 UserOwnsReposException
36 UserOwnsReposException
37
37
38 from sqlalchemy.exc import DatabaseError
38 from sqlalchemy.exc import DatabaseError
39 from rhodecode.lib import generate_api_key
39 from rhodecode.lib import generate_api_key
40 from sqlalchemy.orm import joinedload
40 from sqlalchemy.orm import joinedload
41
41
42 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
43
43
44 PERM_WEIGHTS = {'repository.none': 0,
44 PERM_WEIGHTS = {'repository.none': 0,
45 'repository.read': 1,
45 'repository.read': 1,
46 'repository.write': 3,
46 'repository.write': 3,
47 'repository.admin': 3}
47 'repository.admin': 3}
48
48
49
49
50 class UserModel(BaseModel):
50 class UserModel(BaseModel):
51
51
52 def get(self, user_id, cache=False):
52 def get(self, user_id, cache=False):
53 user = self.sa.query(User)
53 user = self.sa.query(User)
54 if cache:
54 if cache:
55 user = user.options(FromCache("sql_cache_short",
55 user = user.options(FromCache("sql_cache_short",
56 "get_user_%s" % user_id))
56 "get_user_%s" % user_id))
57 return user.get(user_id)
57 return user.get(user_id)
58
58
59 def get_by_username(self, username, cache=False, case_insensitive=False):
59 def get_by_username(self, username, cache=False, case_insensitive=False):
60
60
61 if case_insensitive:
61 if case_insensitive:
62 user = self.sa.query(User).filter(User.username.ilike(username))
62 user = self.sa.query(User).filter(User.username.ilike(username))
63 else:
63 else:
64 user = self.sa.query(User)\
64 user = self.sa.query(User)\
65 .filter(User.username == username)
65 .filter(User.username == username)
66 if cache:
66 if cache:
67 user = user.options(FromCache("sql_cache_short",
67 user = user.options(FromCache("sql_cache_short",
68 "get_user_%s" % username))
68 "get_user_%s" % username))
69 return user.scalar()
69 return user.scalar()
70
70
71 def get_by_api_key(self, api_key, cache=False):
71 def get_by_api_key(self, api_key, cache=False):
72
72
73 user = self.sa.query(User)\
73 user = self.sa.query(User)\
74 .filter(User.api_key == api_key)
74 .filter(User.api_key == api_key)
75 if cache:
75 if cache:
76 user = user.options(FromCache("sql_cache_short",
76 user = user.options(FromCache("sql_cache_short",
77 "get_user_%s" % api_key))
77 "get_user_%s" % api_key))
78 return user.scalar()
78 return user.scalar()
79
79
80 def create(self, form_data):
80 def create(self, form_data):
81 try:
81 try:
82 new_user = User()
82 new_user = User()
83 for k, v in form_data.items():
83 for k, v in form_data.items():
84 setattr(new_user, k, v)
84 setattr(new_user, k, v)
85
85
86 new_user.api_key = generate_api_key(form_data['username'])
86 new_user.api_key = generate_api_key(form_data['username'])
87 self.sa.add(new_user)
87 self.sa.add(new_user)
88 self.sa.commit()
88 self.sa.commit()
89 except:
89 except:
90 log.error(traceback.format_exc())
90 log.error(traceback.format_exc())
91 self.sa.rollback()
91 self.sa.rollback()
92 raise
92 raise
93
93
94 def create_ldap(self, username, password, user_dn, attrs):
94 def create_ldap(self, username, password, user_dn, attrs):
95 """
95 """
96 Checks if user is in database, if not creates this user marked
96 Checks if user is in database, if not creates this user marked
97 as ldap user
97 as ldap user
98 :param username:
98 :param username:
99 :param password:
99 :param password:
100 :param user_dn:
100 :param user_dn:
101 :param attrs:
101 :param attrs:
102 """
102 """
103 from rhodecode.lib.auth import get_crypt_password
103 from rhodecode.lib.auth import get_crypt_password
104 log.debug('Checking for such ldap account in RhodeCode database')
104 log.debug('Checking for such ldap account in RhodeCode database')
105 if self.get_by_username(username, case_insensitive=True) is None:
105 if self.get_by_username(username, case_insensitive=True) is None:
106 try:
106 try:
107 new_user = User()
107 new_user = User()
108 # add ldap account always lowercase
108 # add ldap account always lowercase
109 new_user.username = username.lower()
109 new_user.username = username.lower()
110 new_user.password = get_crypt_password(password)
110 new_user.password = get_crypt_password(password)
111 new_user.api_key = generate_api_key(username)
111 new_user.api_key = generate_api_key(username)
112 new_user.email = attrs['email']
112 new_user.email = attrs['email']
113 new_user.active = True
113 new_user.active = True
114 new_user.ldap_dn = user_dn
114 new_user.ldap_dn = user_dn
115 new_user.name = attrs['name']
115 new_user.name = attrs['name']
116 new_user.lastname = attrs['lastname']
116 new_user.lastname = attrs['lastname']
117
117
118 self.sa.add(new_user)
118 self.sa.add(new_user)
119 self.sa.commit()
119 self.sa.commit()
120 return True
120 return True
121 except (DatabaseError,):
121 except (DatabaseError,):
122 log.error(traceback.format_exc())
122 log.error(traceback.format_exc())
123 self.sa.rollback()
123 self.sa.rollback()
124 raise
124 raise
125 log.debug('this %s user exists skipping creation of ldap account',
125 log.debug('this %s user exists skipping creation of ldap account',
126 username)
126 username)
127 return False
127 return False
128
128
129 def create_registration(self, form_data):
129 def create_registration(self, form_data):
130 from rhodecode.lib.celerylib import tasks, run_task
130 from rhodecode.lib.celerylib import tasks, run_task
131 try:
131 try:
132 new_user = User()
132 new_user = User()
133 for k, v in form_data.items():
133 for k, v in form_data.items():
134 if k != 'admin':
134 if k != 'admin':
135 setattr(new_user, k, v)
135 setattr(new_user, k, v)
136
136
137 self.sa.add(new_user)
137 self.sa.add(new_user)
138 self.sa.commit()
138 self.sa.commit()
139 body = ('New user registration\n'
139 body = ('New user registration\n'
140 'username: %s\n'
140 'username: %s\n'
141 'email: %s\n')
141 'email: %s\n')
142 body = body % (form_data['username'], form_data['email'])
142 body = body % (form_data['username'], form_data['email'])
143
143
144 run_task(tasks.send_email, None,
144 run_task(tasks.send_email, None,
145 _('[RhodeCode] New User registration'),
145 _('[RhodeCode] New User registration'),
146 body)
146 body)
147 except:
147 except:
148 log.error(traceback.format_exc())
148 log.error(traceback.format_exc())
149 self.sa.rollback()
149 self.sa.rollback()
150 raise
150 raise
151
151
152 def update(self, user_id, form_data):
152 def update(self, user_id, form_data):
153 try:
153 try:
154 user = self.get(user_id, cache=False)
154 user = self.get(user_id, cache=False)
155 if user.username == 'default':
155 if user.username == 'default':
156 raise DefaultUserException(
156 raise DefaultUserException(
157 _("You can't Edit this user since it's"
157 _("You can't Edit this user since it's"
158 " crucial for entire application"))
158 " crucial for entire application"))
159
159
160 for k, v in form_data.items():
160 for k, v in form_data.items():
161 if k == 'new_password' and v != '':
161 if k == 'new_password' and v != '':
162 user.password = v
162 user.password = v
163 user.api_key = generate_api_key(user.username)
163 user.api_key = generate_api_key(user.username)
164 else:
164 else:
165 setattr(user, k, v)
165 setattr(user, k, v)
166
166
167 self.sa.add(user)
167 self.sa.add(user)
168 self.sa.commit()
168 self.sa.commit()
169 except:
169 except:
170 log.error(traceback.format_exc())
170 log.error(traceback.format_exc())
171 self.sa.rollback()
171 self.sa.rollback()
172 raise
172 raise
173
173
174 def update_my_account(self, user_id, form_data):
174 def update_my_account(self, user_id, form_data):
175 try:
175 try:
176 user = self.get(user_id, cache=False)
176 user = self.get(user_id, cache=False)
177 if user.username == 'default':
177 if user.username == 'default':
178 raise DefaultUserException(
178 raise DefaultUserException(
179 _("You can't Edit this user since it's"
179 _("You can't Edit this user since it's"
180 " crucial for entire application"))
180 " crucial for entire application"))
181 for k, v in form_data.items():
181 for k, v in form_data.items():
182 if k == 'new_password' and v != '':
182 if k == 'new_password' and v != '':
183 user.password = v
183 user.password = v
184 user.api_key = generate_api_key(user.username)
184 user.api_key = generate_api_key(user.username)
185 else:
185 else:
186 if k not in ['admin', 'active']:
186 if k not in ['admin', 'active']:
187 setattr(user, k, v)
187 setattr(user, k, v)
188
188
189 self.sa.add(user)
189 self.sa.add(user)
190 self.sa.commit()
190 self.sa.commit()
191 except:
191 except:
192 log.error(traceback.format_exc())
192 log.error(traceback.format_exc())
193 self.sa.rollback()
193 self.sa.rollback()
194 raise
194 raise
195
195
196 def delete(self, user_id):
196 def delete(self, user_id):
197 try:
197 try:
198 user = self.get(user_id, cache=False)
198 user = self.get(user_id, cache=False)
199 if user.username == 'default':
199 if user.username == 'default':
200 raise DefaultUserException(
200 raise DefaultUserException(
201 _("You can't remove this user since it's"
201 _("You can't remove this user since it's"
202 " crucial for entire application"))
202 " crucial for entire application"))
203 if user.repositories:
203 if user.repositories:
204 raise UserOwnsReposException(_('This user still owns %s '
204 raise UserOwnsReposException(_('This user still owns %s '
205 'repositories and cannot be '
205 'repositories and cannot be '
206 'removed. Switch owners or '
206 'removed. Switch owners or '
207 'remove those repositories') \
207 'remove those repositories') \
208 % user.repositories)
208 % user.repositories)
209 self.sa.delete(user)
209 self.sa.delete(user)
210 self.sa.commit()
210 self.sa.commit()
211 except:
211 except:
212 log.error(traceback.format_exc())
212 log.error(traceback.format_exc())
213 self.sa.rollback()
213 self.sa.rollback()
214 raise
214 raise
215
215
216 def reset_password_link(self, data):
217 from rhodecode.lib.celerylib import tasks, run_task
218 run_task(tasks.send_password_link, data['email'])
219
216 def reset_password(self, data):
220 def reset_password(self, data):
217 from rhodecode.lib.celerylib import tasks, run_task
221 from rhodecode.lib.celerylib import tasks, run_task
218 run_task(tasks.reset_user_password, data['email'])
222 run_task(tasks.reset_user_password, data['email'])
219
223
220 def fill_data(self, auth_user, user_id=None, api_key=None):
224 def fill_data(self, auth_user, user_id=None, api_key=None):
221 """
225 """
222 Fetches auth_user by user_id,or api_key if present.
226 Fetches auth_user by user_id,or api_key if present.
223 Fills auth_user attributes with those taken from database.
227 Fills auth_user attributes with those taken from database.
224 Additionally set's is_authenitated if lookup fails
228 Additionally set's is_authenitated if lookup fails
225 present in database
229 present in database
226
230
227 :param auth_user: instance of user to set attributes
231 :param auth_user: instance of user to set attributes
228 :param user_id: user id to fetch by
232 :param user_id: user id to fetch by
229 :param api_key: api key to fetch by
233 :param api_key: api key to fetch by
230 """
234 """
231 if user_id is None and api_key is None:
235 if user_id is None and api_key is None:
232 raise Exception('You need to pass user_id or api_key')
236 raise Exception('You need to pass user_id or api_key')
233
237
234 try:
238 try:
235 if api_key:
239 if api_key:
236 dbuser = self.get_by_api_key(api_key)
240 dbuser = self.get_by_api_key(api_key)
237 else:
241 else:
238 dbuser = self.get(user_id)
242 dbuser = self.get(user_id)
239
243
240 if dbuser is not None:
244 if dbuser is not None:
241 log.debug('filling %s data', dbuser)
245 log.debug('filling %s data', dbuser)
242 for k, v in dbuser.get_dict().items():
246 for k, v in dbuser.get_dict().items():
243 setattr(auth_user, k, v)
247 setattr(auth_user, k, v)
244
248
245 except:
249 except:
246 log.error(traceback.format_exc())
250 log.error(traceback.format_exc())
247 auth_user.is_authenticated = False
251 auth_user.is_authenticated = False
248
252
249 return auth_user
253 return auth_user
250
254
251 def fill_perms(self, user):
255 def fill_perms(self, user):
252 """
256 """
253 Fills user permission attribute with permissions taken from database
257 Fills user permission attribute with permissions taken from database
254 works for permissions given for repositories, and for permissions that
258 works for permissions given for repositories, and for permissions that
255 are granted to groups
259 are granted to groups
256
260
257 :param user: user instance to fill his perms
261 :param user: user instance to fill his perms
258 """
262 """
259
263
260 user.permissions['repositories'] = {}
264 user.permissions['repositories'] = {}
261 user.permissions['global'] = set()
265 user.permissions['global'] = set()
262
266
263 #======================================================================
267 #======================================================================
264 # fetch default permissions
268 # fetch default permissions
265 #======================================================================
269 #======================================================================
266 default_user = self.get_by_username('default', cache=True)
270 default_user = self.get_by_username('default', cache=True)
267
271
268 default_perms = self.sa.query(RepoToPerm, Repository, Permission)\
272 default_perms = self.sa.query(RepoToPerm, Repository, Permission)\
269 .join((Repository, RepoToPerm.repository_id ==
273 .join((Repository, RepoToPerm.repository_id ==
270 Repository.repo_id))\
274 Repository.repo_id))\
271 .join((Permission, RepoToPerm.permission_id ==
275 .join((Permission, RepoToPerm.permission_id ==
272 Permission.permission_id))\
276 Permission.permission_id))\
273 .filter(RepoToPerm.user == default_user).all()
277 .filter(RepoToPerm.user == default_user).all()
274
278
275 if user.is_admin:
279 if user.is_admin:
276 #==================================================================
280 #==================================================================
277 # #admin have all default rights set to admin
281 # #admin have all default rights set to admin
278 #==================================================================
282 #==================================================================
279 user.permissions['global'].add('hg.admin')
283 user.permissions['global'].add('hg.admin')
280
284
281 for perm in default_perms:
285 for perm in default_perms:
282 p = 'repository.admin'
286 p = 'repository.admin'
283 user.permissions['repositories'][perm.RepoToPerm.
287 user.permissions['repositories'][perm.RepoToPerm.
284 repository.repo_name] = p
288 repository.repo_name] = p
285
289
286 else:
290 else:
287 #==================================================================
291 #==================================================================
288 # set default permissions
292 # set default permissions
289 #==================================================================
293 #==================================================================
290 uid = user.user_id
294 uid = user.user_id
291
295
292 #default global
296 #default global
293 default_global_perms = self.sa.query(UserToPerm)\
297 default_global_perms = self.sa.query(UserToPerm)\
294 .filter(UserToPerm.user == default_user)
298 .filter(UserToPerm.user == default_user)
295
299
296 for perm in default_global_perms:
300 for perm in default_global_perms:
297 user.permissions['global'].add(perm.permission.permission_name)
301 user.permissions['global'].add(perm.permission.permission_name)
298
302
299 #default for repositories
303 #default for repositories
300 for perm in default_perms:
304 for perm in default_perms:
301 if perm.Repository.private and not (perm.Repository.user_id ==
305 if perm.Repository.private and not (perm.Repository.user_id ==
302 uid):
306 uid):
303 #diself.sable defaults for private repos,
307 #diself.sable defaults for private repos,
304 p = 'repository.none'
308 p = 'repository.none'
305 elif perm.Repository.user_id == uid:
309 elif perm.Repository.user_id == uid:
306 #set admin if owner
310 #set admin if owner
307 p = 'repository.admin'
311 p = 'repository.admin'
308 else:
312 else:
309 p = perm.Permission.permission_name
313 p = perm.Permission.permission_name
310
314
311 user.permissions['repositories'][perm.RepoToPerm.
315 user.permissions['repositories'][perm.RepoToPerm.
312 repository.repo_name] = p
316 repository.repo_name] = p
313
317
314 #==================================================================
318 #==================================================================
315 # overwrite default with user permissions if any
319 # overwrite default with user permissions if any
316 #==================================================================
320 #==================================================================
317
321
318 #user global
322 #user global
319 user_perms = self.sa.query(UserToPerm)\
323 user_perms = self.sa.query(UserToPerm)\
320 .options(joinedload(UserToPerm.permission))\
324 .options(joinedload(UserToPerm.permission))\
321 .filter(UserToPerm.user_id == uid).all()
325 .filter(UserToPerm.user_id == uid).all()
322
326
323 for perm in user_perms:
327 for perm in user_perms:
324 user.permissions['global'].add(perm.permission.
328 user.permissions['global'].add(perm.permission.
325 permission_name)
329 permission_name)
326
330
327 #user repositories
331 #user repositories
328 user_repo_perms = self.sa.query(RepoToPerm, Permission,
332 user_repo_perms = self.sa.query(RepoToPerm, Permission,
329 Repository)\
333 Repository)\
330 .join((Repository, RepoToPerm.repository_id ==
334 .join((Repository, RepoToPerm.repository_id ==
331 Repository.repo_id))\
335 Repository.repo_id))\
332 .join((Permission, RepoToPerm.permission_id ==
336 .join((Permission, RepoToPerm.permission_id ==
333 Permission.permission_id))\
337 Permission.permission_id))\
334 .filter(RepoToPerm.user_id == uid).all()
338 .filter(RepoToPerm.user_id == uid).all()
335
339
336 for perm in user_repo_perms:
340 for perm in user_repo_perms:
337 # set admin if owner
341 # set admin if owner
338 if perm.Repository.user_id == uid:
342 if perm.Repository.user_id == uid:
339 p = 'repository.admin'
343 p = 'repository.admin'
340 else:
344 else:
341 p = perm.Permission.permission_name
345 p = perm.Permission.permission_name
342 user.permissions['repositories'][perm.RepoToPerm.
346 user.permissions['repositories'][perm.RepoToPerm.
343 repository.repo_name] = p
347 repository.repo_name] = p
344
348
345 #==================================================================
349 #==================================================================
346 # check if user is part of groups for this repository and fill in
350 # check if user is part of groups for this repository and fill in
347 # (or replace with higher) permissions
351 # (or replace with higher) permissions
348 #==================================================================
352 #==================================================================
349
353
350 #users group global
354 #users group global
351 user_perms_from_users_groups = self.sa.query(UsersGroupToPerm)\
355 user_perms_from_users_groups = self.sa.query(UsersGroupToPerm)\
352 .options(joinedload(UsersGroupToPerm.permission))\
356 .options(joinedload(UsersGroupToPerm.permission))\
353 .join((UsersGroupMember, UsersGroupToPerm.users_group_id ==
357 .join((UsersGroupMember, UsersGroupToPerm.users_group_id ==
354 UsersGroupMember.users_group_id))\
358 UsersGroupMember.users_group_id))\
355 .filter(UsersGroupMember.user_id == uid).all()
359 .filter(UsersGroupMember.user_id == uid).all()
356
360
357 for perm in user_perms_from_users_groups:
361 for perm in user_perms_from_users_groups:
358 user.permissions['global'].add(perm.permission.permission_name)
362 user.permissions['global'].add(perm.permission.permission_name)
359
363
360 #users group repositories
364 #users group repositories
361 user_repo_perms_from_users_groups = self.sa.query(
365 user_repo_perms_from_users_groups = self.sa.query(
362 UsersGroupRepoToPerm,
366 UsersGroupRepoToPerm,
363 Permission, Repository,)\
367 Permission, Repository,)\
364 .join((Repository, UsersGroupRepoToPerm.repository_id ==
368 .join((Repository, UsersGroupRepoToPerm.repository_id ==
365 Repository.repo_id))\
369 Repository.repo_id))\
366 .join((Permission, UsersGroupRepoToPerm.permission_id ==
370 .join((Permission, UsersGroupRepoToPerm.permission_id ==
367 Permission.permission_id))\
371 Permission.permission_id))\
368 .join((UsersGroupMember, UsersGroupRepoToPerm.users_group_id ==
372 .join((UsersGroupMember, UsersGroupRepoToPerm.users_group_id ==
369 UsersGroupMember.users_group_id))\
373 UsersGroupMember.users_group_id))\
370 .filter(UsersGroupMember.user_id == uid).all()
374 .filter(UsersGroupMember.user_id == uid).all()
371
375
372 for perm in user_repo_perms_from_users_groups:
376 for perm in user_repo_perms_from_users_groups:
373 p = perm.Permission.permission_name
377 p = perm.Permission.permission_name
374 cur_perm = user.permissions['repositories'][perm.
378 cur_perm = user.permissions['repositories'][perm.
375 UsersGroupRepoToPerm.
379 UsersGroupRepoToPerm.
376 repository.repo_name]
380 repository.repo_name]
377 #overwrite permission only if it's greater than permission
381 #overwrite permission only if it's greater than permission
378 # given from other sources
382 # given from other sources
379 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
383 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
380 user.permissions['repositories'][perm.UsersGroupRepoToPerm.
384 user.permissions['repositories'][perm.UsersGroupRepoToPerm.
381 repository.repo_name] = p
385 repository.repo_name] = p
382
386
383 return user
387 return user
@@ -1,2666 +1,2674 b''
1 html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td {
1 html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td {
2 border:0;
2 border:0;
3 outline:0;
3 outline:0;
4 font-size:100%;
4 font-size:100%;
5 vertical-align:baseline;
5 vertical-align:baseline;
6 background:transparent;
6 background:transparent;
7 margin:0;
7 margin:0;
8 padding:0;
8 padding:0;
9 }
9 }
10
10
11 body {
11 body {
12 line-height:1;
12 line-height:1;
13 height:100%;
13 height:100%;
14 background:url("../images/background.png") repeat scroll 0 0 #B0B0B0;
14 background:url("../images/background.png") repeat scroll 0 0 #B0B0B0;
15 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
15 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
16 font-size:12px;
16 font-size:12px;
17 color:#000;
17 color:#000;
18 margin:0;
18 margin:0;
19 padding:0;
19 padding:0;
20 }
20 }
21
21
22 ol,ul {
22 ol,ul {
23 list-style:none;
23 list-style:none;
24 }
24 }
25
25
26 blockquote,q {
26 blockquote,q {
27 quotes:none;
27 quotes:none;
28 }
28 }
29
29
30 blockquote:before,blockquote:after,q:before,q:after {
30 blockquote:before,blockquote:after,q:before,q:after {
31 content:none;
31 content:none;
32 }
32 }
33
33
34 :focus {
34 :focus {
35 outline:0;
35 outline:0;
36 }
36 }
37
37
38 del {
38 del {
39 text-decoration:line-through;
39 text-decoration:line-through;
40 }
40 }
41
41
42 table {
42 table {
43 border-collapse:collapse;
43 border-collapse:collapse;
44 border-spacing:0;
44 border-spacing:0;
45 }
45 }
46
46
47 html {
47 html {
48 height:100%;
48 height:100%;
49 }
49 }
50
50
51 a {
51 a {
52 color:#003367;
52 color:#003367;
53 text-decoration:none;
53 text-decoration:none;
54 cursor:pointer;
54 cursor:pointer;
55 font-weight:700;
55 font-weight:700;
56 }
56 }
57
57
58 a:hover {
58 a:hover {
59 color:#316293;
59 color:#316293;
60 text-decoration:underline;
60 text-decoration:underline;
61 }
61 }
62
62
63 h1,h2,h3,h4,h5,h6 {
63 h1,h2,h3,h4,h5,h6 {
64 color:#292929;
64 color:#292929;
65 font-weight:700;
65 font-weight:700;
66 }
66 }
67
67
68 h1 {
68 h1 {
69 font-size:22px;
69 font-size:22px;
70 }
70 }
71
71
72 h2 {
72 h2 {
73 font-size:20px;
73 font-size:20px;
74 }
74 }
75
75
76 h3 {
76 h3 {
77 font-size:18px;
77 font-size:18px;
78 }
78 }
79
79
80 h4 {
80 h4 {
81 font-size:16px;
81 font-size:16px;
82 }
82 }
83
83
84 h5 {
84 h5 {
85 font-size:14px;
85 font-size:14px;
86 }
86 }
87
87
88 h6 {
88 h6 {
89 font-size:11px;
89 font-size:11px;
90 }
90 }
91
91
92 ul.circle {
92 ul.circle {
93 list-style-type:circle;
93 list-style-type:circle;
94 }
94 }
95
95
96 ul.disc {
96 ul.disc {
97 list-style-type:disc;
97 list-style-type:disc;
98 }
98 }
99
99
100 ul.square {
100 ul.square {
101 list-style-type:square;
101 list-style-type:square;
102 }
102 }
103
103
104 ol.lower-roman {
104 ol.lower-roman {
105 list-style-type:lower-roman;
105 list-style-type:lower-roman;
106 }
106 }
107
107
108 ol.upper-roman {
108 ol.upper-roman {
109 list-style-type:upper-roman;
109 list-style-type:upper-roman;
110 }
110 }
111
111
112 ol.lower-alpha {
112 ol.lower-alpha {
113 list-style-type:lower-alpha;
113 list-style-type:lower-alpha;
114 }
114 }
115
115
116 ol.upper-alpha {
116 ol.upper-alpha {
117 list-style-type:upper-alpha;
117 list-style-type:upper-alpha;
118 }
118 }
119
119
120 ol.decimal {
120 ol.decimal {
121 list-style-type:decimal;
121 list-style-type:decimal;
122 }
122 }
123
123
124 div.color {
124 div.color {
125 clear:both;
125 clear:both;
126 overflow:hidden;
126 overflow:hidden;
127 position:absolute;
127 position:absolute;
128 background:#FFF;
128 background:#FFF;
129 margin:7px 0 0 60px;
129 margin:7px 0 0 60px;
130 padding:1px 1px 1px 0;
130 padding:1px 1px 1px 0;
131 }
131 }
132
132
133 div.color a {
133 div.color a {
134 width:15px;
134 width:15px;
135 height:15px;
135 height:15px;
136 display:block;
136 display:block;
137 float:left;
137 float:left;
138 margin:0 0 0 1px;
138 margin:0 0 0 1px;
139 padding:0;
139 padding:0;
140 }
140 }
141
141
142 div.options {
142 div.options {
143 clear:both;
143 clear:both;
144 overflow:hidden;
144 overflow:hidden;
145 position:absolute;
145 position:absolute;
146 background:#FFF;
146 background:#FFF;
147 margin:7px 0 0 162px;
147 margin:7px 0 0 162px;
148 padding:0;
148 padding:0;
149 }
149 }
150
150
151 div.options a {
151 div.options a {
152 height:1%;
152 height:1%;
153 display:block;
153 display:block;
154 text-decoration:none;
154 text-decoration:none;
155 margin:0;
155 margin:0;
156 padding:3px 8px;
156 padding:3px 8px;
157 }
157 }
158
158
159 .top-left-rounded-corner {
159 .top-left-rounded-corner {
160 -webkit-border-top-left-radius: 8px;
160 -webkit-border-top-left-radius: 8px;
161 -khtml-border-radius-topleft: 8px;
161 -khtml-border-radius-topleft: 8px;
162 -moz-border-radius-topleft: 8px;
162 -moz-border-radius-topleft: 8px;
163 border-top-left-radius: 8px;
163 border-top-left-radius: 8px;
164 }
164 }
165
165
166 .top-right-rounded-corner {
166 .top-right-rounded-corner {
167 -webkit-border-top-right-radius: 8px;
167 -webkit-border-top-right-radius: 8px;
168 -khtml-border-radius-topright: 8px;
168 -khtml-border-radius-topright: 8px;
169 -moz-border-radius-topright: 8px;
169 -moz-border-radius-topright: 8px;
170 border-top-right-radius: 8px;
170 border-top-right-radius: 8px;
171 }
171 }
172
172
173 .bottom-left-rounded-corner {
173 .bottom-left-rounded-corner {
174 -webkit-border-bottom-left-radius: 8px;
174 -webkit-border-bottom-left-radius: 8px;
175 -khtml-border-radius-bottomleft: 8px;
175 -khtml-border-radius-bottomleft: 8px;
176 -moz-border-radius-bottomleft: 8px;
176 -moz-border-radius-bottomleft: 8px;
177 border-bottom-left-radius: 8px;
177 border-bottom-left-radius: 8px;
178 }
178 }
179
179
180 .bottom-right-rounded-corner {
180 .bottom-right-rounded-corner {
181 -webkit-border-bottom-right-radius: 8px;
181 -webkit-border-bottom-right-radius: 8px;
182 -khtml-border-radius-bottomright: 8px;
182 -khtml-border-radius-bottomright: 8px;
183 -moz-border-radius-bottomright: 8px;
183 -moz-border-radius-bottomright: 8px;
184 border-bottom-right-radius: 8px;
184 border-bottom-right-radius: 8px;
185 }
185 }
186
186
187
187
188 #header {
188 #header {
189 margin:0;
189 margin:0;
190 padding:0 10px;
190 padding:0 10px;
191 }
191 }
192
192
193
193
194 #header ul#logged-user{
194 #header ul#logged-user{
195 margin-bottom:5px !important;
195 margin-bottom:5px !important;
196 -webkit-border-radius: 0px 0px 8px 8px;
196 -webkit-border-radius: 0px 0px 8px 8px;
197 -khtml-border-radius: 0px 0px 8px 8px;
197 -khtml-border-radius: 0px 0px 8px 8px;
198 -moz-border-radius: 0px 0px 8px 8px;
198 -moz-border-radius: 0px 0px 8px 8px;
199 border-radius: 0px 0px 8px 8px;
199 border-radius: 0px 0px 8px 8px;
200 height:37px;
200 height:37px;
201 background:url("../images/header_inner.png") repeat-x scroll 0 0 #003367
201 background:url("../images/header_inner.png") repeat-x scroll 0 0 #003367;
202 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
202 }
203 }
203
204
204 #header ul#logged-user li {
205 #header ul#logged-user li {
205 list-style:none;
206 list-style:none;
206 float:left;
207 float:left;
207 margin:8px 0 0;
208 margin:8px 0 0;
208 padding:4px 12px;
209 padding:4px 12px;
209 border-left: 1px solid #316293;
210 border-left: 1px solid #316293;
210 }
211 }
211
212
212 #header ul#logged-user li.first {
213 #header ul#logged-user li.first {
213 border-left:none;
214 border-left:none;
214 margin:4px;
215 margin:4px;
215 }
216 }
216
217
217 #header ul#logged-user li.first div.gravatar {
218 #header ul#logged-user li.first div.gravatar {
218 margin-top:-2px;
219 margin-top:-2px;
219 }
220 }
220
221
221 #header ul#logged-user li.first div.account {
222 #header ul#logged-user li.first div.account {
222 padding-top:4px;
223 padding-top:4px;
223 float:left;
224 float:left;
224 }
225 }
225
226
226 #header ul#logged-user li.last {
227 #header ul#logged-user li.last {
227 border-right:none;
228 border-right:none;
228 }
229 }
229
230
230 #header ul#logged-user li a {
231 #header ul#logged-user li a {
231 color:#fff;
232 color:#fff;
232 font-weight:700;
233 font-weight:700;
233 text-decoration:none;
234 text-decoration:none;
234 }
235 }
235
236
236 #header ul#logged-user li a:hover {
237 #header ul#logged-user li a:hover {
237 text-decoration:underline;
238 text-decoration:underline;
238 }
239 }
239
240
240 #header ul#logged-user li.highlight a {
241 #header ul#logged-user li.highlight a {
241 color:#fff;
242 color:#fff;
242 }
243 }
243
244
244 #header ul#logged-user li.highlight a:hover {
245 #header ul#logged-user li.highlight a:hover {
245 color:#FFF;
246 color:#FFF;
246 }
247 }
247
248
248 #header #header-inner {
249 #header #header-inner {
249 height:40px;
250 height:40px;
250 clear:both;
251 clear:both;
251 position:relative;
252 position:relative;
252 background:#003367 url("../images/header_inner.png") repeat-x;
253 background:#003367 url("../images/header_inner.png") repeat-x;
253 border-bottom:2px solid #fff;
254 border-bottom:2px solid #fff;
254 margin:0;
255 margin:0;
255 padding:0;
256 padding:0;
256 }
257 }
257
258
258 #header #header-inner #home a {
259 #header #header-inner #home a {
259 height:40px;
260 height:40px;
260 width:46px;
261 width:46px;
261 display:block;
262 display:block;
262 background:url("../images/button_home.png");
263 background:url("../images/button_home.png");
263 background-position:0 0;
264 background-position:0 0;
264 margin:0;
265 margin:0;
265 padding:0;
266 padding:0;
266 }
267 }
267
268
268 #header #header-inner #home a:hover {
269 #header #header-inner #home a:hover {
269 background-position:0 -40px;
270 background-position:0 -40px;
270 }
271 }
271
272
272 #header #header-inner #logo h1 {
273 #header #header-inner #logo h1 {
273 color:#FFF;
274 color:#FFF;
274 font-size:18px;
275 font-size:18px;
275 margin:10px 0 0 13px;
276 margin:10px 0 0 13px;
276 padding:0;
277 padding:0;
277 }
278 }
278
279
279 #header #header-inner #logo a {
280 #header #header-inner #logo a {
280 color:#fff;
281 color:#fff;
281 text-decoration:none;
282 text-decoration:none;
282 }
283 }
283
284
284 #header #header-inner #logo a:hover {
285 #header #header-inner #logo a:hover {
285 color:#bfe3ff;
286 color:#bfe3ff;
286 }
287 }
287
288
288 #header #header-inner #quick,#header #header-inner #quick ul {
289 #header #header-inner #quick,#header #header-inner #quick ul {
289 position:relative;
290 position:relative;
290 float:right;
291 float:right;
291 list-style-type:none;
292 list-style-type:none;
292 list-style-position:outside;
293 list-style-position:outside;
293 margin:10px 5px 0 0;
294 margin:10px 5px 0 0;
294 padding:0;
295 padding:0;
295 }
296 }
296
297
297 #header #header-inner #quick li {
298 #header #header-inner #quick li {
298 position:relative;
299 position:relative;
299 float:left;
300 float:left;
300 margin:0 5px 0 0;
301 margin:0 5px 0 0;
301 padding:0;
302 padding:0;
302 }
303 }
303
304
304 #header #header-inner #quick li a {
305 #header #header-inner #quick li a {
305 top:0;
306 top:0;
306 left:0;
307 left:0;
307 height:1%;
308 height:1%;
308 display:block;
309 display:block;
309 clear:both;
310 clear:both;
310 overflow:hidden;
311 overflow:hidden;
311 color:#FFF;
312 color:#FFF;
312 font-weight:700;
313 font-weight:700;
313 text-decoration:none;
314 text-decoration:none;
314 background:#369 url("../images/quick_l.png") no-repeat top left;
315 background:#369 url("../images/quick_l.png") no-repeat top left;
315 padding:0;
316 padding:0;
316 }
317 }
317
318
318 #header #header-inner #quick li span.short {
319 #header #header-inner #quick li span.short {
319 padding:9px 6px 8px 6px;
320 padding:9px 6px 8px 6px;
320 }
321 }
321
322
322 #header #header-inner #quick li span {
323 #header #header-inner #quick li span {
323 top:0;
324 top:0;
324 right:0;
325 right:0;
325 height:1%;
326 height:1%;
326 display:block;
327 display:block;
327 float:left;
328 float:left;
328 background:url("../images/quick_r.png") no-repeat top right;
329 background:url("../images/quick_r.png") no-repeat top right;
329 border-left:1px solid #3f6f9f;
330 border-left:1px solid #3f6f9f;
330 margin:0;
331 margin:0;
331 padding:10px 12px 8px 10px;
332 padding:10px 12px 8px 10px;
332 }
333 }
333
334
334 #header #header-inner #quick li span.normal {
335 #header #header-inner #quick li span.normal {
335 border:none;
336 border:none;
336 padding:10px 12px 8px;
337 padding:10px 12px 8px;
337 }
338 }
338
339
339 #header #header-inner #quick li span.icon {
340 #header #header-inner #quick li span.icon {
340 top:0;
341 top:0;
341 left:0;
342 left:0;
342 border-left:none;
343 border-left:none;
343 background:url("../images/quick_l.png") no-repeat top left;
344 background:url("../images/quick_l.png") no-repeat top left;
344 border-right:1px solid #2e5c89;
345 border-right:1px solid #2e5c89;
345 padding:8px 8px 4px;
346 padding:8px 8px 4px;
346 }
347 }
347
348
348 #header #header-inner #quick li span.icon_short {
349 #header #header-inner #quick li span.icon_short {
349 top:0;
350 top:0;
350 left:0;
351 left:0;
351 border-left:none;
352 border-left:none;
352 background:url("../images/quick_l.png") no-repeat top left;
353 background:url("../images/quick_l.png") no-repeat top left;
353 border-right:1px solid #2e5c89;
354 border-right:1px solid #2e5c89;
354 padding:9px 4px 4px;
355 padding:9px 4px 4px;
355 }
356 }
356
357
357 #header #header-inner #quick li a:hover {
358 #header #header-inner #quick li a:hover {
358 background:#4e4e4e url("../images/quick_l_selected.png") no-repeat top left;
359 background:#4e4e4e url("../images/quick_l_selected.png") no-repeat top left;
359 }
360 }
360
361
361 #header #header-inner #quick li a:hover span {
362 #header #header-inner #quick li a:hover span {
362 border-left:1px solid #545454;
363 border-left:1px solid #545454;
363 background:url("../images/quick_r_selected.png") no-repeat top right;
364 background:url("../images/quick_r_selected.png") no-repeat top right;
364 }
365 }
365
366
366 #header #header-inner #quick li a:hover span.icon,#header #header-inner #quick li a:hover span.icon_short {
367 #header #header-inner #quick li a:hover span.icon,#header #header-inner #quick li a:hover span.icon_short {
367 border-left:none;
368 border-left:none;
368 border-right:1px solid #464646;
369 border-right:1px solid #464646;
369 background:url("../images/quick_l_selected.png") no-repeat top left;
370 background:url("../images/quick_l_selected.png") no-repeat top left;
370 }
371 }
371
372
372
373
373 #header #header-inner #quick ul {
374 #header #header-inner #quick ul {
374 top:29px;
375 top:29px;
375 right:0;
376 right:0;
376 min-width:200px;
377 min-width:200px;
377 display:none;
378 display:none;
378 position:absolute;
379 position:absolute;
379 background:#FFF;
380 background:#FFF;
380 border:1px solid #666;
381 border:1px solid #666;
381 border-top:1px solid #003367;
382 border-top:1px solid #003367;
382 z-index:100;
383 z-index:100;
383 margin:0;
384 margin:0;
384 padding:0;
385 padding:0;
385 }
386 }
386
387
387 #header #header-inner #quick ul.repo_switcher {
388 #header #header-inner #quick ul.repo_switcher {
388 max-height:275px;
389 max-height:275px;
389 overflow-x:hidden;
390 overflow-x:hidden;
390 overflow-y:auto;
391 overflow-y:auto;
391 }
392 }
392 #header #header-inner #quick ul.repo_switcher li.qfilter_rs {
393 #header #header-inner #quick ul.repo_switcher li.qfilter_rs {
393 float:none;
394 float:none;
394 margin:0;
395 margin:0;
395 border-bottom:2px solid #003367;
396 border-bottom:2px solid #003367;
396 }
397 }
397
398
398
399
399 #header #header-inner #quick .repo_switcher_type{
400 #header #header-inner #quick .repo_switcher_type{
400 position:absolute;
401 position:absolute;
401 left:0;
402 left:0;
402 top:9px;
403 top:9px;
403
404
404 }
405 }
405 #header #header-inner #quick li ul li {
406 #header #header-inner #quick li ul li {
406 border-bottom:1px solid #ddd;
407 border-bottom:1px solid #ddd;
407 }
408 }
408
409
409 #header #header-inner #quick li ul li a {
410 #header #header-inner #quick li ul li a {
410 width:182px;
411 width:182px;
411 height:auto;
412 height:auto;
412 display:block;
413 display:block;
413 float:left;
414 float:left;
414 background:#FFF;
415 background:#FFF;
415 color:#003367;
416 color:#003367;
416 font-weight:400;
417 font-weight:400;
417 margin:0;
418 margin:0;
418 padding:7px 9px;
419 padding:7px 9px;
419 }
420 }
420
421
421 #header #header-inner #quick li ul li a:hover {
422 #header #header-inner #quick li ul li a:hover {
422 color:#000;
423 color:#000;
423 background:#FFF;
424 background:#FFF;
424 }
425 }
425
426
426 #header #header-inner #quick ul ul {
427 #header #header-inner #quick ul ul {
427 top:auto;
428 top:auto;
428 }
429 }
429
430
430 #header #header-inner #quick li ul ul {
431 #header #header-inner #quick li ul ul {
431 right:200px;
432 right:200px;
432 max-height:275px;
433 max-height:275px;
433 overflow:auto;
434 overflow:auto;
434 overflow-x:hidden;
435 overflow-x:hidden;
435 white-space:normal;
436 white-space:normal;
436 }
437 }
437
438
438 #header #header-inner #quick li ul li a.journal,#header #header-inner #quick li ul li a.journal:hover {
439 #header #header-inner #quick li ul li a.journal,#header #header-inner #quick li ul li a.journal:hover {
439 background:url("../images/icons/book.png") no-repeat scroll 4px 9px #FFF;
440 background:url("../images/icons/book.png") no-repeat scroll 4px 9px #FFF;
440 width:167px;
441 width:167px;
441 margin:0;
442 margin:0;
442 padding:12px 9px 7px 24px;
443 padding:12px 9px 7px 24px;
443 }
444 }
444
445
445 #header #header-inner #quick li ul li a.private_repo,#header #header-inner #quick li ul li a.private_repo:hover {
446 #header #header-inner #quick li ul li a.private_repo,#header #header-inner #quick li ul li a.private_repo:hover {
446 background:url("../images/icons/lock.png") no-repeat scroll 4px 9px #FFF;
447 background:url("../images/icons/lock.png") no-repeat scroll 4px 9px #FFF;
447 min-width:167px;
448 min-width:167px;
448 margin:0;
449 margin:0;
449 padding:12px 9px 7px 24px;
450 padding:12px 9px 7px 24px;
450 }
451 }
451
452
452 #header #header-inner #quick li ul li a.public_repo,#header #header-inner #quick li ul li a.public_repo:hover {
453 #header #header-inner #quick li ul li a.public_repo,#header #header-inner #quick li ul li a.public_repo:hover {
453 background:url("../images/icons/lock_open.png") no-repeat scroll 4px 9px #FFF;
454 background:url("../images/icons/lock_open.png") no-repeat scroll 4px 9px #FFF;
454 min-width:167px;
455 min-width:167px;
455 margin:0;
456 margin:0;
456 padding:12px 9px 7px 24px;
457 padding:12px 9px 7px 24px;
457 }
458 }
458
459
459 #header #header-inner #quick li ul li a.hg,#header #header-inner #quick li ul li a.hg:hover {
460 #header #header-inner #quick li ul li a.hg,#header #header-inner #quick li ul li a.hg:hover {
460 background:url("../images/icons/hgicon.png") no-repeat scroll 4px 9px #FFF;
461 background:url("../images/icons/hgicon.png") no-repeat scroll 4px 9px #FFF;
461 min-width:167px;
462 min-width:167px;
462 margin:0 0 0 14px;
463 margin:0 0 0 14px;
463 padding:12px 9px 7px 24px;
464 padding:12px 9px 7px 24px;
464 }
465 }
465
466
466 #header #header-inner #quick li ul li a.git,#header #header-inner #quick li ul li a.git:hover {
467 #header #header-inner #quick li ul li a.git,#header #header-inner #quick li ul li a.git:hover {
467 background:url("../images/icons/giticon.png") no-repeat scroll 4px 9px #FFF;
468 background:url("../images/icons/giticon.png") no-repeat scroll 4px 9px #FFF;
468 min-width:167px;
469 min-width:167px;
469 margin:0 0 0 14px;
470 margin:0 0 0 14px;
470 padding:12px 9px 7px 24px;
471 padding:12px 9px 7px 24px;
471 }
472 }
472
473
473 #header #header-inner #quick li ul li a.repos,#header #header-inner #quick li ul li a.repos:hover {
474 #header #header-inner #quick li ul li a.repos,#header #header-inner #quick li ul li a.repos:hover {
474 background:url("../images/icons/database_edit.png") no-repeat scroll 4px 9px #FFF;
475 background:url("../images/icons/database_edit.png") no-repeat scroll 4px 9px #FFF;
475 width:167px;
476 width:167px;
476 margin:0;
477 margin:0;
477 padding:12px 9px 7px 24px;
478 padding:12px 9px 7px 24px;
478 }
479 }
479
480
480 #header #header-inner #quick li ul li a.repos_groups,#header #header-inner #quick li ul li a.repos_groups:hover {
481 #header #header-inner #quick li ul li a.repos_groups,#header #header-inner #quick li ul li a.repos_groups:hover {
481 background:url("../images/icons/database_link.png") no-repeat scroll 4px 9px #FFF;
482 background:url("../images/icons/database_link.png") no-repeat scroll 4px 9px #FFF;
482 width:167px;
483 width:167px;
483 margin:0;
484 margin:0;
484 padding:12px 9px 7px 24px;
485 padding:12px 9px 7px 24px;
485 }
486 }
486
487
487 #header #header-inner #quick li ul li a.users,#header #header-inner #quick li ul li a.users:hover {
488 #header #header-inner #quick li ul li a.users,#header #header-inner #quick li ul li a.users:hover {
488 background:#FFF url("../images/icons/user_edit.png") no-repeat 4px 9px;
489 background:#FFF url("../images/icons/user_edit.png") no-repeat 4px 9px;
489 width:167px;
490 width:167px;
490 margin:0;
491 margin:0;
491 padding:12px 9px 7px 24px;
492 padding:12px 9px 7px 24px;
492 }
493 }
493
494
494 #header #header-inner #quick li ul li a.groups,#header #header-inner #quick li ul li a.groups:hover {
495 #header #header-inner #quick li ul li a.groups,#header #header-inner #quick li ul li a.groups:hover {
495 background:#FFF url("../images/icons/group_edit.png") no-repeat 4px 9px;
496 background:#FFF url("../images/icons/group_edit.png") no-repeat 4px 9px;
496 width:167px;
497 width:167px;
497 margin:0;
498 margin:0;
498 padding:12px 9px 7px 24px;
499 padding:12px 9px 7px 24px;
499 }
500 }
500
501
501 #header #header-inner #quick li ul li a.settings,#header #header-inner #quick li ul li a.settings:hover {
502 #header #header-inner #quick li ul li a.settings,#header #header-inner #quick li ul li a.settings:hover {
502 background:#FFF url("../images/icons/cog.png") no-repeat 4px 9px;
503 background:#FFF url("../images/icons/cog.png") no-repeat 4px 9px;
503 width:167px;
504 width:167px;
504 margin:0;
505 margin:0;
505 padding:12px 9px 7px 24px;
506 padding:12px 9px 7px 24px;
506 }
507 }
507
508
508 #header #header-inner #quick li ul li a.permissions,#header #header-inner #quick li ul li a.permissions:hover {
509 #header #header-inner #quick li ul li a.permissions,#header #header-inner #quick li ul li a.permissions:hover {
509 background:#FFF url("../images/icons/key.png") no-repeat 4px 9px;
510 background:#FFF url("../images/icons/key.png") no-repeat 4px 9px;
510 width:167px;
511 width:167px;
511 margin:0;
512 margin:0;
512 padding:12px 9px 7px 24px;
513 padding:12px 9px 7px 24px;
513 }
514 }
514
515
515 #header #header-inner #quick li ul li a.ldap,#header #header-inner #quick li ul li a.ldap:hover {
516 #header #header-inner #quick li ul li a.ldap,#header #header-inner #quick li ul li a.ldap:hover {
516 background:#FFF url("../images/icons/server_key.png") no-repeat 4px 9px;
517 background:#FFF url("../images/icons/server_key.png") no-repeat 4px 9px;
517 width:167px;
518 width:167px;
518 margin:0;
519 margin:0;
519 padding:12px 9px 7px 24px;
520 padding:12px 9px 7px 24px;
520 }
521 }
521
522
522 #header #header-inner #quick li ul li a.fork,#header #header-inner #quick li ul li a.fork:hover {
523 #header #header-inner #quick li ul li a.fork,#header #header-inner #quick li ul li a.fork:hover {
523 background:#FFF url("../images/icons/arrow_divide.png") no-repeat 4px 9px;
524 background:#FFF url("../images/icons/arrow_divide.png") no-repeat 4px 9px;
524 width:167px;
525 width:167px;
525 margin:0;
526 margin:0;
526 padding:12px 9px 7px 24px;
527 padding:12px 9px 7px 24px;
527 }
528 }
528
529
529 #header #header-inner #quick li ul li a.search,#header #header-inner #quick li ul li a.search:hover {
530 #header #header-inner #quick li ul li a.search,#header #header-inner #quick li ul li a.search:hover {
530 background:#FFF url("../images/icons/search_16.png") no-repeat 4px 9px;
531 background:#FFF url("../images/icons/search_16.png") no-repeat 4px 9px;
531 width:167px;
532 width:167px;
532 margin:0;
533 margin:0;
533 padding:12px 9px 7px 24px;
534 padding:12px 9px 7px 24px;
534 }
535 }
535
536
536 #header #header-inner #quick li ul li a.delete,#header #header-inner #quick li ul li a.delete:hover {
537 #header #header-inner #quick li ul li a.delete,#header #header-inner #quick li ul li a.delete:hover {
537 background:#FFF url("../images/icons/delete.png") no-repeat 4px 9px;
538 background:#FFF url("../images/icons/delete.png") no-repeat 4px 9px;
538 width:167px;
539 width:167px;
539 margin:0;
540 margin:0;
540 padding:12px 9px 7px 24px;
541 padding:12px 9px 7px 24px;
541 }
542 }
542
543
543 #header #header-inner #quick li ul li a.branches,#header #header-inner #quick li ul li a.branches:hover {
544 #header #header-inner #quick li ul li a.branches,#header #header-inner #quick li ul li a.branches:hover {
544 background:#FFF url("../images/icons/arrow_branch.png") no-repeat 4px 9px;
545 background:#FFF url("../images/icons/arrow_branch.png") no-repeat 4px 9px;
545 width:167px;
546 width:167px;
546 margin:0;
547 margin:0;
547 padding:12px 9px 7px 24px;
548 padding:12px 9px 7px 24px;
548 }
549 }
549
550
550 #header #header-inner #quick li ul li a.tags,#header #header-inner #quick li ul li a.tags:hover {
551 #header #header-inner #quick li ul li a.tags,#header #header-inner #quick li ul li a.tags:hover {
551 background:#FFF url("../images/icons/tag_blue.png") no-repeat 4px 9px;
552 background:#FFF url("../images/icons/tag_blue.png") no-repeat 4px 9px;
552 width:167px;
553 width:167px;
553 margin:0;
554 margin:0;
554 padding:12px 9px 7px 24px;
555 padding:12px 9px 7px 24px;
555 }
556 }
556
557
557 #header #header-inner #quick li ul li a.admin,#header #header-inner #quick li ul li a.admin:hover {
558 #header #header-inner #quick li ul li a.admin,#header #header-inner #quick li ul li a.admin:hover {
558 background:#FFF url("../images/icons/cog_edit.png") no-repeat 4px 9px;
559 background:#FFF url("../images/icons/cog_edit.png") no-repeat 4px 9px;
559 width:167px;
560 width:167px;
560 margin:0;
561 margin:0;
561 padding:12px 9px 7px 24px;
562 padding:12px 9px 7px 24px;
562 }
563 }
563
564
564 #content #left {
565 #content #left {
565 left:0;
566 left:0;
566 width:280px;
567 width:280px;
567 position:absolute;
568 position:absolute;
568 }
569 }
569
570
570 #content #right {
571 #content #right {
571 margin:0 60px 10px 290px;
572 margin:0 60px 10px 290px;
572 }
573 }
573
574
574 #content div.box {
575 #content div.box {
575 clear:both;
576 clear:both;
576 overflow:hidden;
577 overflow:hidden;
577 background:#fff;
578 background:#fff;
578 margin:0 0 10px;
579 margin:0 0 10px;
579 padding:0 0 10px;
580 padding:0 0 10px;
580 }
581 }
581
582
582 #content div.box-left {
583 #content div.box-left {
583 width:49%;
584 width:49%;
584 clear:none;
585 clear:none;
585 float:left;
586 float:left;
586 margin:0 0 10px;
587 margin:0 0 10px;
587 }
588 }
588
589
589 #content div.box-right {
590 #content div.box-right {
590 width:49%;
591 width:49%;
591 clear:none;
592 clear:none;
592 float:right;
593 float:right;
593 margin:0 0 10px;
594 margin:0 0 10px;
594 }
595 }
595
596
596 #content div.box div.title {
597 #content div.box div.title {
597 clear:both;
598 clear:both;
598 overflow:hidden;
599 overflow:hidden;
599 background:#369 url("../images/header_inner.png") repeat-x;
600 background:#369 url("../images/header_inner.png") repeat-x;
600 margin:0 0 20px;
601 margin:0 0 20px;
601 padding:0;
602 padding:0;
602 }
603 }
603
604
604 #content div.box div.title h5 {
605 #content div.box div.title h5 {
605 float:left;
606 float:left;
606 border:none;
607 border:none;
607 color:#fff;
608 color:#fff;
608 text-transform:uppercase;
609 text-transform:uppercase;
609 margin:0;
610 margin:0;
610 padding:11px 0 11px 10px;
611 padding:11px 0 11px 10px;
611 }
612 }
612
613
613 #content div.box div.title ul.links li {
614 #content div.box div.title ul.links li {
614 list-style:none;
615 list-style:none;
615 float:left;
616 float:left;
616 margin:0;
617 margin:0;
617 padding:0;
618 padding:0;
618 }
619 }
619
620
620 #content div.box div.title ul.links li a {
621 #content div.box div.title ul.links li a {
621 border-left: 1px solid #316293;
622 border-left: 1px solid #316293;
622 color: #FFFFFF;
623 color: #FFFFFF;
623 display: block;
624 display: block;
624 float: left;
625 float: left;
625 font-size: 13px;
626 font-size: 13px;
626 font-weight: 700;
627 font-weight: 700;
627 height: 1%;
628 height: 1%;
628 margin: 0;
629 margin: 0;
629 padding: 11px 22px 12px;
630 padding: 11px 22px 12px;
630 text-decoration: none;
631 text-decoration: none;
631 }
632 }
632
633
633 #content div.box h1,#content div.box h2,#content div.box h3,#content div.box h4,#content div.box h5,#content div.box h6 {
634 #content div.box h1,#content div.box h2,#content div.box h3,#content div.box h4,#content div.box h5,#content div.box h6 {
634 clear:both;
635 clear:both;
635 overflow:hidden;
636 overflow:hidden;
636 border-bottom:1px solid #DDD;
637 border-bottom:1px solid #DDD;
637 margin:10px 20px;
638 margin:10px 20px;
638 padding:0 0 15px;
639 padding:0 0 15px;
639 }
640 }
640
641
641 #content div.box p {
642 #content div.box p {
642 color:#5f5f5f;
643 color:#5f5f5f;
643 font-size:12px;
644 font-size:12px;
644 line-height:150%;
645 line-height:150%;
645 margin:0 24px 10px;
646 margin:0 24px 10px;
646 padding:0;
647 padding:0;
647 }
648 }
648
649
649 #content div.box blockquote {
650 #content div.box blockquote {
650 border-left:4px solid #DDD;
651 border-left:4px solid #DDD;
651 color:#5f5f5f;
652 color:#5f5f5f;
652 font-size:11px;
653 font-size:11px;
653 line-height:150%;
654 line-height:150%;
654 margin:0 34px;
655 margin:0 34px;
655 padding:0 0 0 14px;
656 padding:0 0 0 14px;
656 }
657 }
657
658
658 #content div.box blockquote p {
659 #content div.box blockquote p {
659 margin:10px 0;
660 margin:10px 0;
660 padding:0;
661 padding:0;
661 }
662 }
662
663
663 #content div.box dl {
664 #content div.box dl {
664 margin:10px 24px;
665 margin:10px 24px;
665 }
666 }
666
667
667 #content div.box dt {
668 #content div.box dt {
668 font-size:12px;
669 font-size:12px;
669 margin:0;
670 margin:0;
670 }
671 }
671
672
672 #content div.box dd {
673 #content div.box dd {
673 font-size:12px;
674 font-size:12px;
674 margin:0;
675 margin:0;
675 padding:8px 0 8px 15px;
676 padding:8px 0 8px 15px;
676 }
677 }
677
678
678 #content div.box li {
679 #content div.box li {
679 font-size:12px;
680 font-size:12px;
680 padding:4px 0;
681 padding:4px 0;
681 }
682 }
682
683
683 #content div.box ul.disc,#content div.box ul.circle {
684 #content div.box ul.disc,#content div.box ul.circle {
684 margin:10px 24px 10px 38px;
685 margin:10px 24px 10px 38px;
685 }
686 }
686
687
687 #content div.box ul.square {
688 #content div.box ul.square {
688 margin:10px 24px 10px 40px;
689 margin:10px 24px 10px 40px;
689 }
690 }
690
691
691 #content div.box img.left {
692 #content div.box img.left {
692 border:none;
693 border:none;
693 float:left;
694 float:left;
694 margin:10px 10px 10px 0;
695 margin:10px 10px 10px 0;
695 }
696 }
696
697
697 #content div.box img.right {
698 #content div.box img.right {
698 border:none;
699 border:none;
699 float:right;
700 float:right;
700 margin:10px 0 10px 10px;
701 margin:10px 0 10px 10px;
701 }
702 }
702
703
703 #content div.box div.messages {
704 #content div.box div.messages {
704 clear:both;
705 clear:both;
705 overflow:hidden;
706 overflow:hidden;
706 margin:0 20px;
707 margin:0 20px;
707 padding:0;
708 padding:0;
708 }
709 }
709
710
710 #content div.box div.message {
711 #content div.box div.message {
711 clear:both;
712 clear:both;
712 overflow:hidden;
713 overflow:hidden;
713 margin:0;
714 margin:0;
714 padding:10px 0;
715 padding:10px 0;
715 }
716 }
716
717
717 #content div.box div.message a {
718 #content div.box div.message a {
718 font-weight:400 !important;
719 font-weight:400 !important;
719 }
720 }
720
721
721 #content div.box div.message div.image {
722 #content div.box div.message div.image {
722 float:left;
723 float:left;
723 margin:9px 0 0 5px;
724 margin:9px 0 0 5px;
724 padding:6px;
725 padding:6px;
725 }
726 }
726
727
727 #content div.box div.message div.image img {
728 #content div.box div.message div.image img {
728 vertical-align:middle;
729 vertical-align:middle;
729 margin:0;
730 margin:0;
730 }
731 }
731
732
732 #content div.box div.message div.text {
733 #content div.box div.message div.text {
733 float:left;
734 float:left;
734 margin:0;
735 margin:0;
735 padding:9px 6px;
736 padding:9px 6px;
736 }
737 }
737
738
738 #content div.box div.message div.dismiss a {
739 #content div.box div.message div.dismiss a {
739 height:16px;
740 height:16px;
740 width:16px;
741 width:16px;
741 display:block;
742 display:block;
742 background:url("../images/icons/cross.png") no-repeat;
743 background:url("../images/icons/cross.png") no-repeat;
743 margin:15px 14px 0 0;
744 margin:15px 14px 0 0;
744 padding:0;
745 padding:0;
745 }
746 }
746
747
747 #content div.box div.message div.text h1,#content div.box div.message div.text h2,#content div.box div.message div.text h3,#content div.box div.message div.text h4,#content div.box div.message div.text h5,#content div.box div.message div.text h6 {
748 #content div.box div.message div.text h1,#content div.box div.message div.text h2,#content div.box div.message div.text h3,#content div.box div.message div.text h4,#content div.box div.message div.text h5,#content div.box div.message div.text h6 {
748 border:none;
749 border:none;
749 margin:0;
750 margin:0;
750 padding:0;
751 padding:0;
751 }
752 }
752
753
753 #content div.box div.message div.text span {
754 #content div.box div.message div.text span {
754 height:1%;
755 height:1%;
755 display:block;
756 display:block;
756 margin:0;
757 margin:0;
757 padding:5px 0 0;
758 padding:5px 0 0;
758 }
759 }
759
760
760 #content div.box div.message-error {
761 #content div.box div.message-error {
761 height:1%;
762 height:1%;
762 clear:both;
763 clear:both;
763 overflow:hidden;
764 overflow:hidden;
764 background:#FBE3E4;
765 background:#FBE3E4;
765 border:1px solid #FBC2C4;
766 border:1px solid #FBC2C4;
766 color:#860006;
767 color:#860006;
767 }
768 }
768
769
769 #content div.box div.message-error h6 {
770 #content div.box div.message-error h6 {
770 color:#860006;
771 color:#860006;
771 }
772 }
772
773
773 #content div.box div.message-warning {
774 #content div.box div.message-warning {
774 height:1%;
775 height:1%;
775 clear:both;
776 clear:both;
776 overflow:hidden;
777 overflow:hidden;
777 background:#FFF6BF;
778 background:#FFF6BF;
778 border:1px solid #FFD324;
779 border:1px solid #FFD324;
779 color:#5f5200;
780 color:#5f5200;
780 }
781 }
781
782
782 #content div.box div.message-warning h6 {
783 #content div.box div.message-warning h6 {
783 color:#5f5200;
784 color:#5f5200;
784 }
785 }
785
786
786 #content div.box div.message-notice {
787 #content div.box div.message-notice {
787 height:1%;
788 height:1%;
788 clear:both;
789 clear:both;
789 overflow:hidden;
790 overflow:hidden;
790 background:#8FBDE0;
791 background:#8FBDE0;
791 border:1px solid #6BACDE;
792 border:1px solid #6BACDE;
792 color:#003863;
793 color:#003863;
793 }
794 }
794
795
795 #content div.box div.message-notice h6 {
796 #content div.box div.message-notice h6 {
796 color:#003863;
797 color:#003863;
797 }
798 }
798
799
799 #content div.box div.message-success {
800 #content div.box div.message-success {
800 height:1%;
801 height:1%;
801 clear:both;
802 clear:both;
802 overflow:hidden;
803 overflow:hidden;
803 background:#E6EFC2;
804 background:#E6EFC2;
804 border:1px solid #C6D880;
805 border:1px solid #C6D880;
805 color:#4e6100;
806 color:#4e6100;
806 }
807 }
807
808
808 #content div.box div.message-success h6 {
809 #content div.box div.message-success h6 {
809 color:#4e6100;
810 color:#4e6100;
810 }
811 }
811
812
812 #content div.box div.form div.fields div.field {
813 #content div.box div.form div.fields div.field {
813 height:1%;
814 height:1%;
814 border-bottom:1px solid #DDD;
815 border-bottom:1px solid #DDD;
815 clear:both;
816 clear:both;
816 margin:0;
817 margin:0;
817 padding:10px 0;
818 padding:10px 0;
818 }
819 }
819
820
820 #content div.box div.form div.fields div.field-first {
821 #content div.box div.form div.fields div.field-first {
821 padding:0 0 10px;
822 padding:0 0 10px;
822 }
823 }
823
824
824 #content div.box div.form div.fields div.field-noborder {
825 #content div.box div.form div.fields div.field-noborder {
825 border-bottom:0 !important;
826 border-bottom:0 !important;
826 }
827 }
827
828
828 #content div.box div.form div.fields div.field span.error-message {
829 #content div.box div.form div.fields div.field span.error-message {
829 height:1%;
830 height:1%;
830 display:inline-block;
831 display:inline-block;
831 color:red;
832 color:red;
832 margin:8px 0 0 4px;
833 margin:8px 0 0 4px;
833 padding:0;
834 padding:0;
834 }
835 }
835
836
836 #content div.box div.form div.fields div.field span.success {
837 #content div.box div.form div.fields div.field span.success {
837 height:1%;
838 height:1%;
838 display:block;
839 display:block;
839 color:#316309;
840 color:#316309;
840 margin:8px 0 0;
841 margin:8px 0 0;
841 padding:0;
842 padding:0;
842 }
843 }
843
844
844 #content div.box div.form div.fields div.field div.label {
845 #content div.box div.form div.fields div.field div.label {
845 left:70px;
846 left:70px;
846 width:auto;
847 width:auto;
847 position:absolute;
848 position:absolute;
848 margin:0;
849 margin:0;
849 padding:8px 0 0 5px;
850 padding:8px 0 0 5px;
850 }
851 }
851
852
852 #content div.box-left div.form div.fields div.field div.label,#content div.box-right div.form div.fields div.field div.label {
853 #content div.box-left div.form div.fields div.field div.label,#content div.box-right div.form div.fields div.field div.label {
853 clear:both;
854 clear:both;
854 overflow:hidden;
855 overflow:hidden;
855 left:0;
856 left:0;
856 width:auto;
857 width:auto;
857 position:relative;
858 position:relative;
858 margin:0;
859 margin:0;
859 padding:0 0 8px;
860 padding:0 0 8px;
860 }
861 }
861
862
862 #content div.box div.form div.fields div.field div.label-select {
863 #content div.box div.form div.fields div.field div.label-select {
863 padding:5px 0 0 5px;
864 padding:5px 0 0 5px;
864 }
865 }
865
866
866 #content div.box-left div.form div.fields div.field div.label-select,#content div.box-right div.form div.fields div.field div.label-select {
867 #content div.box-left div.form div.fields div.field div.label-select,#content div.box-right div.form div.fields div.field div.label-select {
867 padding:0 0 8px;
868 padding:0 0 8px;
868 }
869 }
869
870
870 #content div.box-left div.form div.fields div.field div.label-textarea,#content div.box-right div.form div.fields div.field div.label-textarea {
871 #content div.box-left div.form div.fields div.field div.label-textarea,#content div.box-right div.form div.fields div.field div.label-textarea {
871 padding:0 0 8px !important;
872 padding:0 0 8px !important;
872 }
873 }
873
874
874 #content div.box div.form div.fields div.field div.label label, div.label label{
875 #content div.box div.form div.fields div.field div.label label, div.label label{
875 color:#393939;
876 color:#393939;
876 font-weight:700;
877 font-weight:700;
877 }
878 }
878
879
879 #content div.box div.form div.fields div.field div.input {
880 #content div.box div.form div.fields div.field div.input {
880 margin:0 0 0 200px;
881 margin:0 0 0 200px;
881 }
882 }
882 #content div.box-left div.form div.fields div.field div.input,#content div.box-right div.form div.fields div.field div.input {
883 #content div.box-left div.form div.fields div.field div.input,#content div.box-right div.form div.fields div.field div.input {
883 margin:0 0 0 0px;
884 margin:0 0 0 0px;
884 }
885 }
885
886
886 #content div.box div.form div.fields div.field div.input input {
887 #content div.box div.form div.fields div.field div.input input {
887 background:#FFF;
888 background:#FFF;
888 border-top:1px solid #b3b3b3;
889 border-top:1px solid #b3b3b3;
889 border-left:1px solid #b3b3b3;
890 border-left:1px solid #b3b3b3;
890 border-right:1px solid #eaeaea;
891 border-right:1px solid #eaeaea;
891 border-bottom:1px solid #eaeaea;
892 border-bottom:1px solid #eaeaea;
892 color:#000;
893 color:#000;
893 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
894 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
894 font-size:11px;
895 font-size:11px;
895 margin:0;
896 margin:0;
896 padding:7px 7px 6px;
897 padding:7px 7px 6px;
897 }
898 }
898
899
899
900
900
901
901 #content div.box div.form div.fields div.field div.input input.small {
902 #content div.box div.form div.fields div.field div.input input.small {
902 width:30%;
903 width:30%;
903 }
904 }
904
905
905 #content div.box div.form div.fields div.field div.input input.medium {
906 #content div.box div.form div.fields div.field div.input input.medium {
906 width:55%;
907 width:55%;
907 }
908 }
908
909
909 #content div.box div.form div.fields div.field div.input input.large {
910 #content div.box div.form div.fields div.field div.input input.large {
910 width:85%;
911 width:85%;
911 }
912 }
912
913
913 #content div.box div.form div.fields div.field div.input input.date {
914 #content div.box div.form div.fields div.field div.input input.date {
914 width:177px;
915 width:177px;
915 }
916 }
916
917
917 #content div.box div.form div.fields div.field div.input input.button {
918 #content div.box div.form div.fields div.field div.input input.button {
918 background:#D4D0C8;
919 background:#D4D0C8;
919 border-top:1px solid #FFF;
920 border-top:1px solid #FFF;
920 border-left:1px solid #FFF;
921 border-left:1px solid #FFF;
921 border-right:1px solid #404040;
922 border-right:1px solid #404040;
922 border-bottom:1px solid #404040;
923 border-bottom:1px solid #404040;
923 color:#000;
924 color:#000;
924 margin:0;
925 margin:0;
925 padding:4px 8px;
926 padding:4px 8px;
926 }
927 }
927
928
928 #content div.box div.form div.fields div.field div.textarea {
929 #content div.box div.form div.fields div.field div.textarea {
929 border-top:1px solid #b3b3b3;
930 border-top:1px solid #b3b3b3;
930 border-left:1px solid #b3b3b3;
931 border-left:1px solid #b3b3b3;
931 border-right:1px solid #eaeaea;
932 border-right:1px solid #eaeaea;
932 border-bottom:1px solid #eaeaea;
933 border-bottom:1px solid #eaeaea;
933 margin:0 0 0 200px;
934 margin:0 0 0 200px;
934 padding:10px;
935 padding:10px;
935 }
936 }
936
937
937 #content div.box div.form div.fields div.field div.textarea-editor {
938 #content div.box div.form div.fields div.field div.textarea-editor {
938 border:1px solid #ddd;
939 border:1px solid #ddd;
939 padding:0;
940 padding:0;
940 }
941 }
941
942
942 #content div.box div.form div.fields div.field div.textarea textarea {
943 #content div.box div.form div.fields div.field div.textarea textarea {
943 width:100%;
944 width:100%;
944 height:220px;
945 height:220px;
945 overflow:hidden;
946 overflow:hidden;
946 background:#FFF;
947 background:#FFF;
947 color:#000;
948 color:#000;
948 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
949 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
949 font-size:11px;
950 font-size:11px;
950 outline:none;
951 outline:none;
951 border-width:0;
952 border-width:0;
952 margin:0;
953 margin:0;
953 padding:0;
954 padding:0;
954 }
955 }
955
956
956 #content div.box-left div.form div.fields div.field div.textarea textarea,#content div.box-right div.form div.fields div.field div.textarea textarea {
957 #content div.box-left div.form div.fields div.field div.textarea textarea,#content div.box-right div.form div.fields div.field div.textarea textarea {
957 width:100%;
958 width:100%;
958 height:100px;
959 height:100px;
959 }
960 }
960
961
961 #content div.box div.form div.fields div.field div.textarea table {
962 #content div.box div.form div.fields div.field div.textarea table {
962 width:100%;
963 width:100%;
963 border:none;
964 border:none;
964 margin:0;
965 margin:0;
965 padding:0;
966 padding:0;
966 }
967 }
967
968
968 #content div.box div.form div.fields div.field div.textarea table td {
969 #content div.box div.form div.fields div.field div.textarea table td {
969 background:#DDD;
970 background:#DDD;
970 border:none;
971 border:none;
971 padding:0;
972 padding:0;
972 }
973 }
973
974
974 #content div.box div.form div.fields div.field div.textarea table td table {
975 #content div.box div.form div.fields div.field div.textarea table td table {
975 width:auto;
976 width:auto;
976 border:none;
977 border:none;
977 margin:0;
978 margin:0;
978 padding:0;
979 padding:0;
979 }
980 }
980
981
981 #content div.box div.form div.fields div.field div.textarea table td table td {
982 #content div.box div.form div.fields div.field div.textarea table td table td {
982 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
983 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
983 font-size:11px;
984 font-size:11px;
984 padding:5px 5px 5px 0;
985 padding:5px 5px 5px 0;
985 }
986 }
986
987
987 #content div.box div.form div.fields div.field input[type=text]:focus,#content div.box div.form div.fields div.field input[type=password]:focus,#content div.box div.form div.fields div.field input[type=file]:focus,#content div.box div.form div.fields div.field textarea:focus,#content div.box div.form div.fields div.field select:focus {
988 #content div.box div.form div.fields div.field input[type=text]:focus,#content div.box div.form div.fields div.field input[type=password]:focus,#content div.box div.form div.fields div.field input[type=file]:focus,#content div.box div.form div.fields div.field textarea:focus,#content div.box div.form div.fields div.field select:focus {
988 background:#f6f6f6;
989 background:#f6f6f6;
989 border-color:#666;
990 border-color:#666;
990 }
991 }
991
992
992 div.form div.fields div.field div.button {
993 div.form div.fields div.field div.button {
993 margin:0;
994 margin:0;
994 padding:0 0 0 8px;
995 padding:0 0 0 8px;
995 }
996 }
996
997
997 div.form div.fields div.field div.highlight .ui-button {
998 div.form div.fields div.field div.highlight .ui-button {
998 background:#4e85bb url("../images/button_highlight.png") repeat-x;
999 background:#4e85bb url("../images/button_highlight.png") repeat-x;
999 border-top:1px solid #5c91a4;
1000 border-top:1px solid #5c91a4;
1000 border-left:1px solid #2a6f89;
1001 border-left:1px solid #2a6f89;
1001 border-right:1px solid #2b7089;
1002 border-right:1px solid #2b7089;
1002 border-bottom:1px solid #1a6480;
1003 border-bottom:1px solid #1a6480;
1003 color:#FFF;
1004 color:#FFF;
1004 margin:0;
1005 margin:0;
1005 padding:6px 12px;
1006 padding:6px 12px;
1006 }
1007 }
1007
1008
1008 div.form div.fields div.field div.highlight .ui-state-hover {
1009 div.form div.fields div.field div.highlight .ui-state-hover {
1009 background:#46a0c1 url("../images/button_highlight_selected.png") repeat-x;
1010 background:#46a0c1 url("../images/button_highlight_selected.png") repeat-x;
1010 border-top:1px solid #78acbf;
1011 border-top:1px solid #78acbf;
1011 border-left:1px solid #34819e;
1012 border-left:1px solid #34819e;
1012 border-right:1px solid #35829f;
1013 border-right:1px solid #35829f;
1013 border-bottom:1px solid #257897;
1014 border-bottom:1px solid #257897;
1014 color:#FFF;
1015 color:#FFF;
1015 margin:0;
1016 margin:0;
1016 padding:6px 12px;
1017 padding:6px 12px;
1017 }
1018 }
1018
1019
1019 #content div.box div.form div.fields div.buttons div.highlight input.ui-button {
1020 #content div.box div.form div.fields div.buttons div.highlight input.ui-button {
1020 background:#4e85bb url("../images/button_highlight.png") repeat-x;
1021 background:#4e85bb url("../images/button_highlight.png") repeat-x;
1021 border-top:1px solid #5c91a4;
1022 border-top:1px solid #5c91a4;
1022 border-left:1px solid #2a6f89;
1023 border-left:1px solid #2a6f89;
1023 border-right:1px solid #2b7089;
1024 border-right:1px solid #2b7089;
1024 border-bottom:1px solid #1a6480;
1025 border-bottom:1px solid #1a6480;
1025 color:#fff;
1026 color:#fff;
1026 margin:0;
1027 margin:0;
1027 padding:6px 12px;
1028 padding:6px 12px;
1028 }
1029 }
1029
1030
1030 #content div.box div.form div.fields div.buttons div.highlight input.ui-state-hover {
1031 #content div.box div.form div.fields div.buttons div.highlight input.ui-state-hover {
1031 background:#46a0c1 url("../images/button_highlight_selected.png") repeat-x;
1032 background:#46a0c1 url("../images/button_highlight_selected.png") repeat-x;
1032 border-top:1px solid #78acbf;
1033 border-top:1px solid #78acbf;
1033 border-left:1px solid #34819e;
1034 border-left:1px solid #34819e;
1034 border-right:1px solid #35829f;
1035 border-right:1px solid #35829f;
1035 border-bottom:1px solid #257897;
1036 border-bottom:1px solid #257897;
1036 color:#fff;
1037 color:#fff;
1037 margin:0;
1038 margin:0;
1038 padding:6px 12px;
1039 padding:6px 12px;
1039 }
1040 }
1040
1041
1041 #content div.box table {
1042 #content div.box table {
1042 width:100%;
1043 width:100%;
1043 border-collapse:collapse;
1044 border-collapse:collapse;
1044 margin:0;
1045 margin:0;
1045 padding:0;
1046 padding:0;
1046 }
1047 }
1047
1048
1048 #content div.box table th {
1049 #content div.box table th {
1049 background:#eee;
1050 background:#eee;
1050 border-bottom:1px solid #ddd;
1051 border-bottom:1px solid #ddd;
1051 padding:5px 0px 5px 5px;
1052 padding:5px 0px 5px 5px;
1052 }
1053 }
1053
1054
1054 #content div.box table th.left {
1055 #content div.box table th.left {
1055 text-align:left;
1056 text-align:left;
1056 }
1057 }
1057
1058
1058 #content div.box table th.right {
1059 #content div.box table th.right {
1059 text-align:right;
1060 text-align:right;
1060 }
1061 }
1061
1062
1062 #content div.box table th.center {
1063 #content div.box table th.center {
1063 text-align:center;
1064 text-align:center;
1064 }
1065 }
1065
1066
1066 #content div.box table th.selected {
1067 #content div.box table th.selected {
1067 vertical-align:middle;
1068 vertical-align:middle;
1068 padding:0;
1069 padding:0;
1069 }
1070 }
1070
1071
1071 #content div.box table td {
1072 #content div.box table td {
1072 background:#fff;
1073 background:#fff;
1073 border-bottom:1px solid #cdcdcd;
1074 border-bottom:1px solid #cdcdcd;
1074 vertical-align:middle;
1075 vertical-align:middle;
1075 padding:5px;
1076 padding:5px;
1076 }
1077 }
1077
1078
1078 #content div.box table tr.selected td {
1079 #content div.box table tr.selected td {
1079 background:#FFC;
1080 background:#FFC;
1080 }
1081 }
1081
1082
1082 #content div.box table td.selected {
1083 #content div.box table td.selected {
1083 width:3%;
1084 width:3%;
1084 text-align:center;
1085 text-align:center;
1085 vertical-align:middle;
1086 vertical-align:middle;
1086 padding:0;
1087 padding:0;
1087 }
1088 }
1088
1089
1089 #content div.box table td.action {
1090 #content div.box table td.action {
1090 width:45%;
1091 width:45%;
1091 text-align:left;
1092 text-align:left;
1092 }
1093 }
1093
1094
1094 #content div.box table td.date {
1095 #content div.box table td.date {
1095 width:33%;
1096 width:33%;
1096 text-align:center;
1097 text-align:center;
1097 }
1098 }
1098
1099
1099 #content div.box div.action {
1100 #content div.box div.action {
1100 float:right;
1101 float:right;
1101 background:#FFF;
1102 background:#FFF;
1102 text-align:right;
1103 text-align:right;
1103 margin:10px 0 0;
1104 margin:10px 0 0;
1104 padding:0;
1105 padding:0;
1105 }
1106 }
1106
1107
1107 #content div.box div.action select {
1108 #content div.box div.action select {
1108 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
1109 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
1109 font-size:11px;
1110 font-size:11px;
1110 margin:0;
1111 margin:0;
1111 }
1112 }
1112
1113
1113 #content div.box div.action .ui-selectmenu {
1114 #content div.box div.action .ui-selectmenu {
1114 margin:0;
1115 margin:0;
1115 padding:0;
1116 padding:0;
1116 }
1117 }
1117
1118
1118 #content div.box div.pagination {
1119 #content div.box div.pagination {
1119 height:1%;
1120 height:1%;
1120 clear:both;
1121 clear:both;
1121 overflow:hidden;
1122 overflow:hidden;
1122 margin:10px 0 0;
1123 margin:10px 0 0;
1123 padding:0;
1124 padding:0;
1124 }
1125 }
1125
1126
1126 #content div.box div.pagination ul.pager {
1127 #content div.box div.pagination ul.pager {
1127 float:right;
1128 float:right;
1128 text-align:right;
1129 text-align:right;
1129 margin:0;
1130 margin:0;
1130 padding:0;
1131 padding:0;
1131 }
1132 }
1132
1133
1133 #content div.box div.pagination ul.pager li {
1134 #content div.box div.pagination ul.pager li {
1134 height:1%;
1135 height:1%;
1135 float:left;
1136 float:left;
1136 list-style:none;
1137 list-style:none;
1137 background:#ebebeb url("../images/pager.png") repeat-x;
1138 background:#ebebeb url("../images/pager.png") repeat-x;
1138 border-top:1px solid #dedede;
1139 border-top:1px solid #dedede;
1139 border-left:1px solid #cfcfcf;
1140 border-left:1px solid #cfcfcf;
1140 border-right:1px solid #c4c4c4;
1141 border-right:1px solid #c4c4c4;
1141 border-bottom:1px solid #c4c4c4;
1142 border-bottom:1px solid #c4c4c4;
1142 color:#4A4A4A;
1143 color:#4A4A4A;
1143 font-weight:700;
1144 font-weight:700;
1144 margin:0 0 0 4px;
1145 margin:0 0 0 4px;
1145 padding:0;
1146 padding:0;
1146 }
1147 }
1147
1148
1148 #content div.box div.pagination ul.pager li.separator {
1149 #content div.box div.pagination ul.pager li.separator {
1149 padding:6px;
1150 padding:6px;
1150 }
1151 }
1151
1152
1152 #content div.box div.pagination ul.pager li.current {
1153 #content div.box div.pagination ul.pager li.current {
1153 background:#b4b4b4 url("../images/pager_selected.png") repeat-x;
1154 background:#b4b4b4 url("../images/pager_selected.png") repeat-x;
1154 border-top:1px solid #ccc;
1155 border-top:1px solid #ccc;
1155 border-left:1px solid #bebebe;
1156 border-left:1px solid #bebebe;
1156 border-right:1px solid #b1b1b1;
1157 border-right:1px solid #b1b1b1;
1157 border-bottom:1px solid #afafaf;
1158 border-bottom:1px solid #afafaf;
1158 color:#515151;
1159 color:#515151;
1159 padding:6px;
1160 padding:6px;
1160 }
1161 }
1161
1162
1162 #content div.box div.pagination ul.pager li a {
1163 #content div.box div.pagination ul.pager li a {
1163 height:1%;
1164 height:1%;
1164 display:block;
1165 display:block;
1165 float:left;
1166 float:left;
1166 color:#515151;
1167 color:#515151;
1167 text-decoration:none;
1168 text-decoration:none;
1168 margin:0;
1169 margin:0;
1169 padding:6px;
1170 padding:6px;
1170 }
1171 }
1171
1172
1172 #content div.box div.pagination ul.pager li a:hover,#content div.box div.pagination ul.pager li a:active {
1173 #content div.box div.pagination ul.pager li a:hover,#content div.box div.pagination ul.pager li a:active {
1173 background:#b4b4b4 url("../images/pager_selected.png") repeat-x;
1174 background:#b4b4b4 url("../images/pager_selected.png") repeat-x;
1174 border-top:1px solid #ccc;
1175 border-top:1px solid #ccc;
1175 border-left:1px solid #bebebe;
1176 border-left:1px solid #bebebe;
1176 border-right:1px solid #b1b1b1;
1177 border-right:1px solid #b1b1b1;
1177 border-bottom:1px solid #afafaf;
1178 border-bottom:1px solid #afafaf;
1178 margin:-1px;
1179 margin:-1px;
1179 }
1180 }
1180
1181
1181 #content div.box div.pagination-wh {
1182 #content div.box div.pagination-wh {
1182 height:1%;
1183 height:1%;
1183 clear:both;
1184 clear:both;
1184 overflow:hidden;
1185 overflow:hidden;
1185 text-align:right;
1186 text-align:right;
1186 margin:10px 0 0;
1187 margin:10px 0 0;
1187 padding:0;
1188 padding:0;
1188 }
1189 }
1189
1190
1190 #content div.box div.pagination-right {
1191 #content div.box div.pagination-right {
1191 float:right;
1192 float:right;
1192 }
1193 }
1193
1194
1194 #content div.box div.pagination-wh a,#content div.box div.pagination-wh span.pager_dotdot {
1195 #content div.box div.pagination-wh a,#content div.box div.pagination-wh span.pager_dotdot {
1195 height:1%;
1196 height:1%;
1196 float:left;
1197 float:left;
1197 background:#ebebeb url("../images/pager.png") repeat-x;
1198 background:#ebebeb url("../images/pager.png") repeat-x;
1198 border-top:1px solid #dedede;
1199 border-top:1px solid #dedede;
1199 border-left:1px solid #cfcfcf;
1200 border-left:1px solid #cfcfcf;
1200 border-right:1px solid #c4c4c4;
1201 border-right:1px solid #c4c4c4;
1201 border-bottom:1px solid #c4c4c4;
1202 border-bottom:1px solid #c4c4c4;
1202 color:#4A4A4A;
1203 color:#4A4A4A;
1203 font-weight:700;
1204 font-weight:700;
1204 margin:0 0 0 4px;
1205 margin:0 0 0 4px;
1205 padding:6px;
1206 padding:6px;
1206 }
1207 }
1207
1208
1208 #content div.box div.pagination-wh span.pager_curpage {
1209 #content div.box div.pagination-wh span.pager_curpage {
1209 height:1%;
1210 height:1%;
1210 float:left;
1211 float:left;
1211 background:#b4b4b4 url("../images/pager_selected.png") repeat-x;
1212 background:#b4b4b4 url("../images/pager_selected.png") repeat-x;
1212 border-top:1px solid #ccc;
1213 border-top:1px solid #ccc;
1213 border-left:1px solid #bebebe;
1214 border-left:1px solid #bebebe;
1214 border-right:1px solid #b1b1b1;
1215 border-right:1px solid #b1b1b1;
1215 border-bottom:1px solid #afafaf;
1216 border-bottom:1px solid #afafaf;
1216 color:#515151;
1217 color:#515151;
1217 font-weight:700;
1218 font-weight:700;
1218 margin:0 0 0 4px;
1219 margin:0 0 0 4px;
1219 padding:6px;
1220 padding:6px;
1220 }
1221 }
1221
1222
1222 #content div.box div.pagination-wh a:hover,#content div.box div.pagination-wh a:active {
1223 #content div.box div.pagination-wh a:hover,#content div.box div.pagination-wh a:active {
1223 background:#b4b4b4 url("../images/pager_selected.png") repeat-x;
1224 background:#b4b4b4 url("../images/pager_selected.png") repeat-x;
1224 border-top:1px solid #ccc;
1225 border-top:1px solid #ccc;
1225 border-left:1px solid #bebebe;
1226 border-left:1px solid #bebebe;
1226 border-right:1px solid #b1b1b1;
1227 border-right:1px solid #b1b1b1;
1227 border-bottom:1px solid #afafaf;
1228 border-bottom:1px solid #afafaf;
1228 text-decoration:none;
1229 text-decoration:none;
1229 }
1230 }
1230
1231
1231 #content div.box div.traffic div.legend {
1232 #content div.box div.traffic div.legend {
1232 clear:both;
1233 clear:both;
1233 overflow:hidden;
1234 overflow:hidden;
1234 border-bottom:1px solid #ddd;
1235 border-bottom:1px solid #ddd;
1235 margin:0 0 10px;
1236 margin:0 0 10px;
1236 padding:0 0 10px;
1237 padding:0 0 10px;
1237 }
1238 }
1238
1239
1239 #content div.box div.traffic div.legend h6 {
1240 #content div.box div.traffic div.legend h6 {
1240 float:left;
1241 float:left;
1241 border:none;
1242 border:none;
1242 margin:0;
1243 margin:0;
1243 padding:0;
1244 padding:0;
1244 }
1245 }
1245
1246
1246 #content div.box div.traffic div.legend li {
1247 #content div.box div.traffic div.legend li {
1247 list-style:none;
1248 list-style:none;
1248 float:left;
1249 float:left;
1249 font-size:11px;
1250 font-size:11px;
1250 margin:0;
1251 margin:0;
1251 padding:0 8px 0 4px;
1252 padding:0 8px 0 4px;
1252 }
1253 }
1253
1254
1254 #content div.box div.traffic div.legend li.visits {
1255 #content div.box div.traffic div.legend li.visits {
1255 border-left:12px solid #edc240;
1256 border-left:12px solid #edc240;
1256 }
1257 }
1257
1258
1258 #content div.box div.traffic div.legend li.pageviews {
1259 #content div.box div.traffic div.legend li.pageviews {
1259 border-left:12px solid #afd8f8;
1260 border-left:12px solid #afd8f8;
1260 }
1261 }
1261
1262
1262 #content div.box div.traffic table {
1263 #content div.box div.traffic table {
1263 width:auto;
1264 width:auto;
1264 }
1265 }
1265
1266
1266 #content div.box div.traffic table td {
1267 #content div.box div.traffic table td {
1267 background:transparent;
1268 background:transparent;
1268 border:none;
1269 border:none;
1269 padding:2px 3px 3px;
1270 padding:2px 3px 3px;
1270 }
1271 }
1271
1272
1272 #content div.box div.traffic table td.legendLabel {
1273 #content div.box div.traffic table td.legendLabel {
1273 padding:0 3px 2px;
1274 padding:0 3px 2px;
1274 }
1275 }
1275
1276
1276 #summary{
1277 #summary{
1277
1278
1278 }
1279 }
1279
1280
1280 #summary .desc{
1281 #summary .desc{
1281 white-space: pre;
1282 white-space: pre;
1282 width: 100%;
1283 width: 100%;
1283 }
1284 }
1284
1285
1285 #summary .repo_name{
1286 #summary .repo_name{
1286 font-size: 1.6em;
1287 font-size: 1.6em;
1287 font-weight: bold;
1288 font-weight: bold;
1288 vertical-align: baseline;
1289 vertical-align: baseline;
1289 clear:right
1290 clear:right
1290 }
1291 }
1291
1292
1292
1293
1293 #footer {
1294 #footer {
1294 clear:both;
1295 clear:both;
1295 overflow:hidden;
1296 overflow:hidden;
1296 text-align:right;
1297 text-align:right;
1297 margin:0;
1298 margin:0;
1298 padding:0 10px 4px;
1299 padding:0 10px 4px;
1299 margin:-10px 0 0;
1300 margin:-10px 0 0;
1300 }
1301 }
1301
1302
1302 #footer div#footer-inner {
1303 #footer div#footer-inner {
1303 background:url("../images/header_inner.png") repeat-x scroll 0 0 #003367;
1304 background:url("../images/header_inner.png") repeat-x scroll 0 0 #003367;
1304 border-top:2px solid #FFFFFF;
1305 border-top:2px solid #FFFFFF;
1305 }
1306 }
1306
1307
1307 #footer div#footer-inner p {
1308 #footer div#footer-inner p {
1308 padding:15px 25px 15px 0;
1309 padding:15px 25px 15px 0;
1309 color:#FFF;
1310 color:#FFF;
1310 font-weight:700;
1311 font-weight:700;
1311 }
1312 }
1312 #footer div#footer-inner .footer-link {
1313 #footer div#footer-inner .footer-link {
1313 float:left;
1314 float:left;
1314 padding-left:10px;
1315 padding-left:10px;
1315 }
1316 }
1316 #footer div#footer-inner .footer-link a,#footer div#footer-inner .footer-link-right a {
1317 #footer div#footer-inner .footer-link a,#footer div#footer-inner .footer-link-right a {
1317 color:#FFF;
1318 color:#FFF;
1318 }
1319 }
1319
1320
1320 #login div.title {
1321 #login div.title {
1321 width:420px;
1322 width:420px;
1322 clear:both;
1323 clear:both;
1323 overflow:hidden;
1324 overflow:hidden;
1324 position:relative;
1325 position:relative;
1325 background:#003367 url("../images/header_inner.png") repeat-x;
1326 background:#003367 url("../images/header_inner.png") repeat-x;
1326 margin:0 auto;
1327 margin:0 auto;
1327 padding:0;
1328 padding:0;
1328 }
1329 }
1329
1330
1330 #login div.inner {
1331 #login div.inner {
1331 width:380px;
1332 width:380px;
1332 background:#FFF url("../images/login.png") no-repeat top left;
1333 background:#FFF url("../images/login.png") no-repeat top left;
1333 border-top:none;
1334 border-top:none;
1334 border-bottom:none;
1335 border-bottom:none;
1335 margin:0 auto;
1336 margin:0 auto;
1336 padding:20px;
1337 padding:20px;
1337 }
1338 }
1338
1339
1339 #login div.form div.fields div.field div.label {
1340 #login div.form div.fields div.field div.label {
1340 width:173px;
1341 width:173px;
1341 float:left;
1342 float:left;
1342 text-align:right;
1343 text-align:right;
1343 margin:2px 10px 0 0;
1344 margin:2px 10px 0 0;
1344 padding:5px 0 0 5px;
1345 padding:5px 0 0 5px;
1345 }
1346 }
1346
1347
1347 #login div.form div.fields div.field div.input input {
1348 #login div.form div.fields div.field div.input input {
1348 width:176px;
1349 width:176px;
1349 background:#FFF;
1350 background:#FFF;
1350 border-top:1px solid #b3b3b3;
1351 border-top:1px solid #b3b3b3;
1351 border-left:1px solid #b3b3b3;
1352 border-left:1px solid #b3b3b3;
1352 border-right:1px solid #eaeaea;
1353 border-right:1px solid #eaeaea;
1353 border-bottom:1px solid #eaeaea;
1354 border-bottom:1px solid #eaeaea;
1354 color:#000;
1355 color:#000;
1355 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
1356 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
1356 font-size:11px;
1357 font-size:11px;
1357 margin:0;
1358 margin:0;
1358 padding:7px 7px 6px;
1359 padding:7px 7px 6px;
1359 }
1360 }
1360
1361
1361 #login div.form div.fields div.buttons {
1362 #login div.form div.fields div.buttons {
1362 clear:both;
1363 clear:both;
1363 overflow:hidden;
1364 overflow:hidden;
1364 border-top:1px solid #DDD;
1365 border-top:1px solid #DDD;
1365 text-align:right;
1366 text-align:right;
1366 margin:0;
1367 margin:0;
1367 padding:10px 0 0;
1368 padding:10px 0 0;
1368 }
1369 }
1369
1370
1370 #login div.form div.links {
1371 #login div.form div.links {
1371 clear:both;
1372 clear:both;
1372 overflow:hidden;
1373 overflow:hidden;
1373 margin:10px 0 0;
1374 margin:10px 0 0;
1374 padding:0 0 2px;
1375 padding:0 0 2px;
1375 }
1376 }
1376
1377
1377 #quick_login{
1378 #quick_login{
1378 top: 31px;
1379 top: 31px;
1379 background-color: rgb(0, 51, 103);
1380 background-color: rgb(0, 51, 103);
1380 z-index: 999;
1381 z-index: 999;
1381 height: 150px;
1382 height: 150px;
1382 position: absolute;
1383 position: absolute;
1383 margin-left: -16px;
1384 margin-left: -16px;
1384 width: 281px;
1385 width: 281px;
1385 border-radius: 0 0 8px 8px;
1386 border-radius: 0 0 8px 8px;
1387 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
1388 }
1389
1390 #quick_login .password_forgoten{
1391 padding-right:10px;
1392 padding-top:10px;
1393 float:left;
1386 }
1394 }
1387
1395
1388 #quick_login div.form div.fields{
1396 #quick_login div.form div.fields{
1389 padding-top: 2px;
1397 padding-top: 2px;
1390 padding-left:10px;
1398 padding-left:10px;
1391 }
1399 }
1392
1400
1393 #quick_login div.form div.fields div.field{
1401 #quick_login div.form div.fields div.field{
1394 padding: 5px;
1402 padding: 5px;
1395 }
1403 }
1396
1404
1397 #quick_login div.form div.fields div.field div.label label{
1405 #quick_login div.form div.fields div.field div.label label{
1398 color:#fff;
1406 color:#fff;
1399 padding-bottom: 3px;
1407 padding-bottom: 3px;
1400 }
1408 }
1401
1409
1402 #quick_login div.form div.fields div.field div.input input {
1410 #quick_login div.form div.fields div.field div.input input {
1403 width:236px;
1411 width:236px;
1404 background:#FFF;
1412 background:#FFF;
1405 border-top:1px solid #b3b3b3;
1413 border-top:1px solid #b3b3b3;
1406 border-left:1px solid #b3b3b3;
1414 border-left:1px solid #b3b3b3;
1407 border-right:1px solid #eaeaea;
1415 border-right:1px solid #eaeaea;
1408 border-bottom:1px solid #eaeaea;
1416 border-bottom:1px solid #eaeaea;
1409 color:#000;
1417 color:#000;
1410 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
1418 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
1411 font-size:11px;
1419 font-size:11px;
1412 margin:0;
1420 margin:0;
1413 padding:5px 7px 4px;
1421 padding:5px 7px 4px;
1414 }
1422 }
1415
1423
1416 #quick_login div.form div.fields div.buttons {
1424 #quick_login div.form div.fields div.buttons {
1417 clear:both;
1425 clear:both;
1418 overflow:hidden;
1426 overflow:hidden;
1419 text-align:right;
1427 text-align:right;
1420 margin:0;
1428 margin:0;
1421 padding:10px 14px 0;
1429 padding:10px 14px 0;
1422 }
1430 }
1423
1431
1424 #quick_login div.form div.fields div.buttons input.ui-button{
1432 #quick_login div.form div.fields div.buttons input.ui-button{
1425 background:#e5e3e3 url("../images/button.png") repeat-x;
1433 background:#e5e3e3 url("../images/button.png") repeat-x;
1426 border-top:1px solid #DDD;
1434 border-top:1px solid #DDD;
1427 border-left:1px solid #c6c6c6;
1435 border-left:1px solid #c6c6c6;
1428 border-right:1px solid #DDD;
1436 border-right:1px solid #DDD;
1429 border-bottom:1px solid #c6c6c6;
1437 border-bottom:1px solid #c6c6c6;
1430 color:#515151;
1438 color:#515151;
1431 margin:0;
1439 margin:0;
1432 padding:4px 10px;
1440 padding:4px 10px;
1433 }
1441 }
1434
1442
1435 #quick_login div.form div.links {
1443 #quick_login div.form div.links {
1436 clear:both;
1444 clear:both;
1437 overflow:hidden;
1445 overflow:hidden;
1438 margin:10px 0 0;
1446 margin:10px 0 0;
1439 padding:0 0 2px;
1447 padding:0 0 2px;
1440 }
1448 }
1441
1449
1442 #register div.title {
1450 #register div.title {
1443 clear:both;
1451 clear:both;
1444 overflow:hidden;
1452 overflow:hidden;
1445 position:relative;
1453 position:relative;
1446 background:#003367 url("../images/header_inner.png") repeat-x;
1454 background:#003367 url("../images/header_inner.png") repeat-x;
1447 margin:0 auto;
1455 margin:0 auto;
1448 padding:0;
1456 padding:0;
1449 }
1457 }
1450
1458
1451 #register div.inner {
1459 #register div.inner {
1452 background:#FFF;
1460 background:#FFF;
1453 border-top:none;
1461 border-top:none;
1454 border-bottom:none;
1462 border-bottom:none;
1455 margin:0 auto;
1463 margin:0 auto;
1456 padding:20px;
1464 padding:20px;
1457 }
1465 }
1458
1466
1459 #register div.form div.fields div.field div.label {
1467 #register div.form div.fields div.field div.label {
1460 width:135px;
1468 width:135px;
1461 float:left;
1469 float:left;
1462 text-align:right;
1470 text-align:right;
1463 margin:2px 10px 0 0;
1471 margin:2px 10px 0 0;
1464 padding:5px 0 0 5px;
1472 padding:5px 0 0 5px;
1465 }
1473 }
1466
1474
1467 #register div.form div.fields div.field div.input input {
1475 #register div.form div.fields div.field div.input input {
1468 width:300px;
1476 width:300px;
1469 background:#FFF;
1477 background:#FFF;
1470 border-top:1px solid #b3b3b3;
1478 border-top:1px solid #b3b3b3;
1471 border-left:1px solid #b3b3b3;
1479 border-left:1px solid #b3b3b3;
1472 border-right:1px solid #eaeaea;
1480 border-right:1px solid #eaeaea;
1473 border-bottom:1px solid #eaeaea;
1481 border-bottom:1px solid #eaeaea;
1474 color:#000;
1482 color:#000;
1475 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
1483 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
1476 font-size:11px;
1484 font-size:11px;
1477 margin:0;
1485 margin:0;
1478 padding:7px 7px 6px;
1486 padding:7px 7px 6px;
1479 }
1487 }
1480
1488
1481 #register div.form div.fields div.buttons {
1489 #register div.form div.fields div.buttons {
1482 clear:both;
1490 clear:both;
1483 overflow:hidden;
1491 overflow:hidden;
1484 border-top:1px solid #DDD;
1492 border-top:1px solid #DDD;
1485 text-align:left;
1493 text-align:left;
1486 margin:0;
1494 margin:0;
1487 padding:10px 0 0 150px;
1495 padding:10px 0 0 150px;
1488 }
1496 }
1489
1497
1490 #register div.form div.fields div.buttons div.highlight input.ui-button {
1498 #register div.form div.fields div.buttons div.highlight input.ui-button {
1491 background:url("../images/button_highlight.png") repeat-x scroll 0 0 #4E85BB;
1499 background:url("../images/button_highlight.png") repeat-x scroll 0 0 #4E85BB;
1492 color:#FFF;
1500 color:#FFF;
1493 border-color:#5C91A4 #2B7089 #1A6480 #2A6F89;
1501 border-color:#5C91A4 #2B7089 #1A6480 #2A6F89;
1494 border-style:solid;
1502 border-style:solid;
1495 border-width:1px;
1503 border-width:1px;
1496 }
1504 }
1497
1505
1498 #register div.form div.activation_msg {
1506 #register div.form div.activation_msg {
1499 padding-top:4px;
1507 padding-top:4px;
1500 padding-bottom:4px;
1508 padding-bottom:4px;
1501 }
1509 }
1502
1510
1503 #journal .journal_day{
1511 #journal .journal_day{
1504 font-size:20px;
1512 font-size:20px;
1505 padding:10px 0px;
1513 padding:10px 0px;
1506 border-bottom:2px solid #DDD;
1514 border-bottom:2px solid #DDD;
1507 margin-left:10px;
1515 margin-left:10px;
1508 margin-right:10px;
1516 margin-right:10px;
1509 }
1517 }
1510
1518
1511 #journal .journal_container{
1519 #journal .journal_container{
1512 padding:5px;
1520 padding:5px;
1513 clear:both;
1521 clear:both;
1514 margin:0px 5px 0px 10px;
1522 margin:0px 5px 0px 10px;
1515 }
1523 }
1516
1524
1517 #journal .journal_action_container{
1525 #journal .journal_action_container{
1518 padding-left:38px;
1526 padding-left:38px;
1519 }
1527 }
1520
1528
1521 #journal .journal_user{
1529 #journal .journal_user{
1522 color: #747474;
1530 color: #747474;
1523 font-size: 14px;
1531 font-size: 14px;
1524 font-weight: bold;
1532 font-weight: bold;
1525 height: 30px;
1533 height: 30px;
1526 }
1534 }
1527 #journal .journal_icon{
1535 #journal .journal_icon{
1528 clear: both;
1536 clear: both;
1529 float: left;
1537 float: left;
1530 padding-right: 4px;
1538 padding-right: 4px;
1531 padding-top: 3px;
1539 padding-top: 3px;
1532 }
1540 }
1533 #journal .journal_action{
1541 #journal .journal_action{
1534 padding-top:4px;
1542 padding-top:4px;
1535 min-height:2px;
1543 min-height:2px;
1536 float:left
1544 float:left
1537 }
1545 }
1538 #journal .journal_action_params{
1546 #journal .journal_action_params{
1539 clear: left;
1547 clear: left;
1540 padding-left: 22px;
1548 padding-left: 22px;
1541 }
1549 }
1542 #journal .journal_repo{
1550 #journal .journal_repo{
1543 float: left;
1551 float: left;
1544 margin-left: 6px;
1552 margin-left: 6px;
1545 padding-top: 3px;
1553 padding-top: 3px;
1546 }
1554 }
1547 #journal .date{
1555 #journal .date{
1548 clear: both;
1556 clear: both;
1549 color: #777777;
1557 color: #777777;
1550 font-size: 11px;
1558 font-size: 11px;
1551 padding-left: 22px;
1559 padding-left: 22px;
1552 }
1560 }
1553 #journal .journal_repo .journal_repo_name{
1561 #journal .journal_repo .journal_repo_name{
1554 font-weight: bold;
1562 font-weight: bold;
1555 font-size: 1.1em;
1563 font-size: 1.1em;
1556 }
1564 }
1557 #journal .compare_view{
1565 #journal .compare_view{
1558 padding: 5px 0px 5px 0px;
1566 padding: 5px 0px 5px 0px;
1559 width: 95px;
1567 width: 95px;
1560 }
1568 }
1561 .journal_highlight{
1569 .journal_highlight{
1562 font-weight: bold;
1570 font-weight: bold;
1563 padding: 0 2px;
1571 padding: 0 2px;
1564 vertical-align: bottom;
1572 vertical-align: bottom;
1565 }
1573 }
1566 .trending_language_tbl,.trending_language_tbl td {
1574 .trending_language_tbl,.trending_language_tbl td {
1567 border:0 !important;
1575 border:0 !important;
1568 margin:0 !important;
1576 margin:0 !important;
1569 padding:0 !important;
1577 padding:0 !important;
1570 }
1578 }
1571
1579
1572 .trending_language {
1580 .trending_language {
1573 background-color:#003367;
1581 background-color:#003367;
1574 color:#FFF;
1582 color:#FFF;
1575 display:block;
1583 display:block;
1576 min-width:20px;
1584 min-width:20px;
1577 text-decoration:none;
1585 text-decoration:none;
1578 height:12px;
1586 height:12px;
1579 margin-bottom:4px;
1587 margin-bottom:4px;
1580 margin-left:5px;
1588 margin-left:5px;
1581 white-space:pre;
1589 white-space:pre;
1582 padding:3px;
1590 padding:3px;
1583 }
1591 }
1584
1592
1585 h3.files_location {
1593 h3.files_location {
1586 font-size:1.8em;
1594 font-size:1.8em;
1587 font-weight:700;
1595 font-weight:700;
1588 border-bottom:none !important;
1596 border-bottom:none !important;
1589 margin:10px 0 !important;
1597 margin:10px 0 !important;
1590 }
1598 }
1591
1599
1592 #files_data dl dt {
1600 #files_data dl dt {
1593 float:left;
1601 float:left;
1594 width:115px;
1602 width:115px;
1595 margin:0 !important;
1603 margin:0 !important;
1596 padding:5px;
1604 padding:5px;
1597 }
1605 }
1598
1606
1599 #files_data dl dd {
1607 #files_data dl dd {
1600 margin:0 !important;
1608 margin:0 !important;
1601 padding:5px !important;
1609 padding:5px !important;
1602 }
1610 }
1603
1611
1604 #changeset_content {
1612 #changeset_content {
1605 border:1px solid #CCC;
1613 border:1px solid #CCC;
1606 padding:5px;
1614 padding:5px;
1607 }
1615 }
1608 #changeset_compare_view_content{
1616 #changeset_compare_view_content{
1609 border:1px solid #CCC;
1617 border:1px solid #CCC;
1610 padding:5px;
1618 padding:5px;
1611 }
1619 }
1612
1620
1613 #changeset_content .container {
1621 #changeset_content .container {
1614 min-height:120px;
1622 min-height:120px;
1615 font-size:1.2em;
1623 font-size:1.2em;
1616 overflow:hidden;
1624 overflow:hidden;
1617 }
1625 }
1618
1626
1619 #changeset_compare_view_content .compare_view_commits{
1627 #changeset_compare_view_content .compare_view_commits{
1620 width: auto !important;
1628 width: auto !important;
1621 }
1629 }
1622
1630
1623 #changeset_compare_view_content .compare_view_commits td{
1631 #changeset_compare_view_content .compare_view_commits td{
1624 padding:0px 0px 0px 12px !important;
1632 padding:0px 0px 0px 12px !important;
1625 }
1633 }
1626
1634
1627 #changeset_content .container .right {
1635 #changeset_content .container .right {
1628 float:right;
1636 float:right;
1629 width:25%;
1637 width:25%;
1630 text-align:right;
1638 text-align:right;
1631 }
1639 }
1632
1640
1633 #changeset_content .container .left .message {
1641 #changeset_content .container .left .message {
1634 font-style:italic;
1642 font-style:italic;
1635 color:#556CB5;
1643 color:#556CB5;
1636 white-space:pre-wrap;
1644 white-space:pre-wrap;
1637 }
1645 }
1638
1646
1639 .cs_files .cur_cs{
1647 .cs_files .cur_cs{
1640 margin:10px 2px;
1648 margin:10px 2px;
1641 font-weight: bold;
1649 font-weight: bold;
1642 }
1650 }
1643
1651
1644 .cs_files .node{
1652 .cs_files .node{
1645 float: left;
1653 float: left;
1646 }
1654 }
1647 .cs_files .changes{
1655 .cs_files .changes{
1648 float: right;
1656 float: right;
1649 }
1657 }
1650 .cs_files .changes .added{
1658 .cs_files .changes .added{
1651 background-color: #BBFFBB;
1659 background-color: #BBFFBB;
1652 float: left;
1660 float: left;
1653 text-align: center;
1661 text-align: center;
1654 font-size: 90%;
1662 font-size: 90%;
1655 }
1663 }
1656 .cs_files .changes .deleted{
1664 .cs_files .changes .deleted{
1657 background-color: #FF8888;
1665 background-color: #FF8888;
1658 float: left;
1666 float: left;
1659 text-align: center;
1667 text-align: center;
1660 font-size: 90%;
1668 font-size: 90%;
1661 }
1669 }
1662 .cs_files .cs_added {
1670 .cs_files .cs_added {
1663 background:url("../images/icons/page_white_add.png") no-repeat scroll 3px;
1671 background:url("../images/icons/page_white_add.png") no-repeat scroll 3px;
1664 height:16px;
1672 height:16px;
1665 padding-left:20px;
1673 padding-left:20px;
1666 margin-top:7px;
1674 margin-top:7px;
1667 text-align:left;
1675 text-align:left;
1668 }
1676 }
1669
1677
1670 .cs_files .cs_changed {
1678 .cs_files .cs_changed {
1671 background:url("../images/icons/page_white_edit.png") no-repeat scroll 3px;
1679 background:url("../images/icons/page_white_edit.png") no-repeat scroll 3px;
1672 height:16px;
1680 height:16px;
1673 padding-left:20px;
1681 padding-left:20px;
1674 margin-top:7px;
1682 margin-top:7px;
1675 text-align:left;
1683 text-align:left;
1676 }
1684 }
1677
1685
1678 .cs_files .cs_removed {
1686 .cs_files .cs_removed {
1679 background:url("../images/icons/page_white_delete.png") no-repeat scroll 3px;
1687 background:url("../images/icons/page_white_delete.png") no-repeat scroll 3px;
1680 height:16px;
1688 height:16px;
1681 padding-left:20px;
1689 padding-left:20px;
1682 margin-top:7px;
1690 margin-top:7px;
1683 text-align:left;
1691 text-align:left;
1684 }
1692 }
1685
1693
1686 #graph {
1694 #graph {
1687 overflow:hidden;
1695 overflow:hidden;
1688 }
1696 }
1689
1697
1690 #graph_nodes {
1698 #graph_nodes {
1691 width:160px;
1699 width:160px;
1692 float:left;
1700 float:left;
1693 margin-left:-50px;
1701 margin-left:-50px;
1694 margin-top:5px;
1702 margin-top:5px;
1695 }
1703 }
1696
1704
1697 #graph_content {
1705 #graph_content {
1698 width:800px;
1706 width:800px;
1699 float:left;
1707 float:left;
1700 }
1708 }
1701
1709
1702 #graph_content .container_header {
1710 #graph_content .container_header {
1703 border:1px solid #CCC;
1711 border:1px solid #CCC;
1704 padding:10px;
1712 padding:10px;
1705 }
1713 }
1706 #graph_content #rev_range_container{
1714 #graph_content #rev_range_container{
1707 padding:10px 0px;
1715 padding:10px 0px;
1708 }
1716 }
1709 #graph_content .container {
1717 #graph_content .container {
1710 border-bottom:1px solid #CCC;
1718 border-bottom:1px solid #CCC;
1711 border-left:1px solid #CCC;
1719 border-left:1px solid #CCC;
1712 border-right:1px solid #CCC;
1720 border-right:1px solid #CCC;
1713 min-height:80px;
1721 min-height:80px;
1714 overflow:hidden;
1722 overflow:hidden;
1715 font-size:1.2em;
1723 font-size:1.2em;
1716 }
1724 }
1717
1725
1718 #graph_content .container .right {
1726 #graph_content .container .right {
1719 float:right;
1727 float:right;
1720 width:28%;
1728 width:28%;
1721 text-align:right;
1729 text-align:right;
1722 padding-bottom:5px;
1730 padding-bottom:5px;
1723 }
1731 }
1724
1732
1725 #graph_content .container .left .date {
1733 #graph_content .container .left .date {
1726 font-weight:700;
1734 font-weight:700;
1727 padding-bottom:5px;
1735 padding-bottom:5px;
1728 }
1736 }
1729 #graph_content .container .left .date span{
1737 #graph_content .container .left .date span{
1730 vertical-align: text-top;
1738 vertical-align: text-top;
1731 }
1739 }
1732
1740
1733 #graph_content .container .left .message {
1741 #graph_content .container .left .message {
1734 font-size:100%;
1742 font-size:100%;
1735 padding-top:3px;
1743 padding-top:3px;
1736 white-space:pre-wrap;
1744 white-space:pre-wrap;
1737 }
1745 }
1738
1746
1739 .right div {
1747 .right div {
1740 clear:both;
1748 clear:both;
1741 }
1749 }
1742
1750
1743 .right .changes .added,.changed,.removed {
1751 .right .changes .added,.changed,.removed {
1744 border:1px solid #DDD;
1752 border:1px solid #DDD;
1745 display:block;
1753 display:block;
1746 float:right;
1754 float:right;
1747 text-align:center;
1755 text-align:center;
1748 min-width:15px;
1756 min-width:15px;
1749 cursor: help;
1757 cursor: help;
1750 }
1758 }
1751 .right .changes .large {
1759 .right .changes .large {
1752 border:1px solid #DDD;
1760 border:1px solid #DDD;
1753 display:block;
1761 display:block;
1754 float:right;
1762 float:right;
1755 text-align:center;
1763 text-align:center;
1756 min-width:45px;
1764 min-width:45px;
1757 cursor: help;
1765 cursor: help;
1758 background: #54A9F7;
1766 background: #54A9F7;
1759 }
1767 }
1760
1768
1761 .right .changes .added {
1769 .right .changes .added {
1762 background:#BFB;
1770 background:#BFB;
1763 }
1771 }
1764
1772
1765 .right .changes .changed {
1773 .right .changes .changed {
1766 background:#FD8;
1774 background:#FD8;
1767 }
1775 }
1768
1776
1769 .right .changes .removed {
1777 .right .changes .removed {
1770 background:#F88;
1778 background:#F88;
1771 }
1779 }
1772
1780
1773 .right .merge {
1781 .right .merge {
1774 vertical-align:top;
1782 vertical-align:top;
1775 font-size:0.75em;
1783 font-size:0.75em;
1776 font-weight:700;
1784 font-weight:700;
1777 }
1785 }
1778
1786
1779 .right .parent {
1787 .right .parent {
1780 font-size:90%;
1788 font-size:90%;
1781 font-family:monospace;
1789 font-family:monospace;
1782 }
1790 }
1783
1791
1784 .right .logtags .branchtag {
1792 .right .logtags .branchtag {
1785 background:#FFF url("../images/icons/arrow_branch.png") no-repeat right 6px;
1793 background:#FFF url("../images/icons/arrow_branch.png") no-repeat right 6px;
1786 display:block;
1794 display:block;
1787 font-size:0.8em;
1795 font-size:0.8em;
1788 padding:11px 16px 0 0;
1796 padding:11px 16px 0 0;
1789 }
1797 }
1790
1798
1791 .right .logtags .tagtag {
1799 .right .logtags .tagtag {
1792 background:#FFF url("../images/icons/tag_blue.png") no-repeat right 6px;
1800 background:#FFF url("../images/icons/tag_blue.png") no-repeat right 6px;
1793 display:block;
1801 display:block;
1794 font-size:0.8em;
1802 font-size:0.8em;
1795 padding:11px 16px 0 0;
1803 padding:11px 16px 0 0;
1796 }
1804 }
1797
1805
1798 div.browserblock {
1806 div.browserblock {
1799 overflow:hidden;
1807 overflow:hidden;
1800 border:1px solid #ccc;
1808 border:1px solid #ccc;
1801 background:#f8f8f8;
1809 background:#f8f8f8;
1802 font-size:100%;
1810 font-size:100%;
1803 line-height:125%;
1811 line-height:125%;
1804 padding:0;
1812 padding:0;
1805 }
1813 }
1806
1814
1807 div.browserblock .browser-header {
1815 div.browserblock .browser-header {
1808 background:#FFF;
1816 background:#FFF;
1809 padding:10px 0px 25px 0px;
1817 padding:10px 0px 25px 0px;
1810 width: 100%;
1818 width: 100%;
1811 }
1819 }
1812 div.browserblock .browser-nav {
1820 div.browserblock .browser-nav {
1813 float:left
1821 float:left
1814 }
1822 }
1815
1823
1816 div.browserblock .browser-branch {
1824 div.browserblock .browser-branch {
1817 float:left;
1825 float:left;
1818 }
1826 }
1819
1827
1820 div.browserblock .browser-branch label {
1828 div.browserblock .browser-branch label {
1821 color:#4A4A4A;
1829 color:#4A4A4A;
1822 vertical-align:text-top;
1830 vertical-align:text-top;
1823 }
1831 }
1824
1832
1825 div.browserblock .browser-header span {
1833 div.browserblock .browser-header span {
1826 margin-left:5px;
1834 margin-left:5px;
1827 font-weight:700;
1835 font-weight:700;
1828 }
1836 }
1829
1837
1830 div.browserblock .browser-body {
1838 div.browserblock .browser-body {
1831 background:#EEE;
1839 background:#EEE;
1832 border-top:1px solid #CCC;
1840 border-top:1px solid #CCC;
1833 }
1841 }
1834
1842
1835 table.code-browser {
1843 table.code-browser {
1836 border-collapse:collapse;
1844 border-collapse:collapse;
1837 width:100%;
1845 width:100%;
1838 }
1846 }
1839
1847
1840 table.code-browser tr {
1848 table.code-browser tr {
1841 margin:3px;
1849 margin:3px;
1842 }
1850 }
1843
1851
1844 table.code-browser thead th {
1852 table.code-browser thead th {
1845 background-color:#EEE;
1853 background-color:#EEE;
1846 height:20px;
1854 height:20px;
1847 font-size:1.1em;
1855 font-size:1.1em;
1848 font-weight:700;
1856 font-weight:700;
1849 text-align:left;
1857 text-align:left;
1850 padding-left:10px;
1858 padding-left:10px;
1851 }
1859 }
1852
1860
1853 table.code-browser tbody td {
1861 table.code-browser tbody td {
1854 padding-left:10px;
1862 padding-left:10px;
1855 height:20px;
1863 height:20px;
1856 }
1864 }
1857
1865
1858 table.code-browser .browser-file {
1866 table.code-browser .browser-file {
1859 background:url("../images/icons/document_16.png") no-repeat scroll 3px;
1867 background:url("../images/icons/document_16.png") no-repeat scroll 3px;
1860 height:16px;
1868 height:16px;
1861 padding-left:20px;
1869 padding-left:20px;
1862 text-align:left;
1870 text-align:left;
1863 }
1871 }
1864 .diffblock .changeset_file{
1872 .diffblock .changeset_file{
1865 background:url("../images/icons/file.png") no-repeat scroll 3px;
1873 background:url("../images/icons/file.png") no-repeat scroll 3px;
1866 height:16px;
1874 height:16px;
1867 padding-left:22px;
1875 padding-left:22px;
1868 text-align:left;
1876 text-align:left;
1869 font-size: 14px;
1877 font-size: 14px;
1870 }
1878 }
1871
1879
1872 .diffblock .changeset_header{
1880 .diffblock .changeset_header{
1873 margin-left: 6px !important;
1881 margin-left: 6px !important;
1874 }
1882 }
1875
1883
1876 table.code-browser .browser-dir {
1884 table.code-browser .browser-dir {
1877 background:url("../images/icons/folder_16.png") no-repeat scroll 3px;
1885 background:url("../images/icons/folder_16.png") no-repeat scroll 3px;
1878 height:16px;
1886 height:16px;
1879 padding-left:20px;
1887 padding-left:20px;
1880 text-align:left;
1888 text-align:left;
1881 }
1889 }
1882
1890
1883 .box .search {
1891 .box .search {
1884 clear:both;
1892 clear:both;
1885 overflow:hidden;
1893 overflow:hidden;
1886 margin:0;
1894 margin:0;
1887 padding:0 20px 10px;
1895 padding:0 20px 10px;
1888 }
1896 }
1889
1897
1890 .box .search div.search_path {
1898 .box .search div.search_path {
1891 background:none repeat scroll 0 0 #EEE;
1899 background:none repeat scroll 0 0 #EEE;
1892 border:1px solid #CCC;
1900 border:1px solid #CCC;
1893 color:blue;
1901 color:blue;
1894 margin-bottom:10px;
1902 margin-bottom:10px;
1895 padding:10px 0;
1903 padding:10px 0;
1896 }
1904 }
1897
1905
1898 .box .search div.search_path div.link {
1906 .box .search div.search_path div.link {
1899 font-weight:700;
1907 font-weight:700;
1900 margin-left:25px;
1908 margin-left:25px;
1901 }
1909 }
1902
1910
1903 .box .search div.search_path div.link a {
1911 .box .search div.search_path div.link a {
1904 color:#003367;
1912 color:#003367;
1905 cursor:pointer;
1913 cursor:pointer;
1906 text-decoration:none;
1914 text-decoration:none;
1907 }
1915 }
1908
1916
1909 #path_unlock {
1917 #path_unlock {
1910 color:red;
1918 color:red;
1911 font-size:1.2em;
1919 font-size:1.2em;
1912 padding-left:4px;
1920 padding-left:4px;
1913 }
1921 }
1914
1922
1915 .info_box span {
1923 .info_box span {
1916 margin-left:3px;
1924 margin-left:3px;
1917 margin-right:3px;
1925 margin-right:3px;
1918 }
1926 }
1919
1927
1920 .info_box .rev {
1928 .info_box .rev {
1921 color: #003367;
1929 color: #003367;
1922 font-size: 1.6em;
1930 font-size: 1.6em;
1923 font-weight: bold;
1931 font-weight: bold;
1924 vertical-align: sub;
1932 vertical-align: sub;
1925 }
1933 }
1926
1934
1927
1935
1928 .info_box input#at_rev,.info_box input#size {
1936 .info_box input#at_rev,.info_box input#size {
1929 background:#FFF;
1937 background:#FFF;
1930 border-top:1px solid #b3b3b3;
1938 border-top:1px solid #b3b3b3;
1931 border-left:1px solid #b3b3b3;
1939 border-left:1px solid #b3b3b3;
1932 border-right:1px solid #eaeaea;
1940 border-right:1px solid #eaeaea;
1933 border-bottom:1px solid #eaeaea;
1941 border-bottom:1px solid #eaeaea;
1934 color:#000;
1942 color:#000;
1935 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
1943 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
1936 font-size:12px;
1944 font-size:12px;
1937 margin:0;
1945 margin:0;
1938 padding:1px 5px 1px;
1946 padding:1px 5px 1px;
1939 }
1947 }
1940
1948
1941
1949
1942
1950
1943 .info_box input#view {
1951 .info_box input#view {
1944 text-align:center;
1952 text-align:center;
1945 padding:4px 3px 2px 2px;
1953 padding:4px 3px 2px 2px;
1946 }
1954 }
1947
1955
1948 .yui-overlay,.yui-panel-container {
1956 .yui-overlay,.yui-panel-container {
1949 visibility:hidden;
1957 visibility:hidden;
1950 position:absolute;
1958 position:absolute;
1951 z-index:2;
1959 z-index:2;
1952 }
1960 }
1953
1961
1954 .yui-tt {
1962 .yui-tt {
1955 visibility:hidden;
1963 visibility:hidden;
1956 position:absolute;
1964 position:absolute;
1957 color:#666;
1965 color:#666;
1958 background-color:#FFF;
1966 background-color:#FFF;
1959 font-family:arial, helvetica, verdana, sans-serif;
1967 font-family:arial, helvetica, verdana, sans-serif;
1960 border:2px solid #003367;
1968 border:2px solid #003367;
1961 font:100% sans-serif;
1969 font:100% sans-serif;
1962 width:auto;
1970 width:auto;
1963 opacity:1px;
1971 opacity:1px;
1964 padding:8px;
1972 padding:8px;
1965 white-space: pre-wrap;
1973 white-space: pre-wrap;
1966 -webkit-border-radius: 8px 8px 8px 8px;
1974 -webkit-border-radius: 8px 8px 8px 8px;
1967 -khtml-border-radius: 8px 8px 8px 8px;
1975 -khtml-border-radius: 8px 8px 8px 8px;
1968 -moz-border-radius: 8px 8px 8px 8px;
1976 -moz-border-radius: 8px 8px 8px 8px;
1969 border-radius: 8px 8px 8px 8px;
1977 border-radius: 8px 8px 8px 8px;
1970
1978
1971 }
1979 }
1972
1980
1973 .ac {
1981 .ac {
1974 vertical-align:top;
1982 vertical-align:top;
1975 }
1983 }
1976
1984
1977 .ac .yui-ac {
1985 .ac .yui-ac {
1978 position:relative;
1986 position:relative;
1979 font-family:arial;
1987 font-family:arial;
1980 font-size:100%;
1988 font-size:100%;
1981 }
1989 }
1982
1990
1983 .ac .perm_ac {
1991 .ac .perm_ac {
1984 width:15em;
1992 width:15em;
1985 }
1993 }
1986
1994
1987 .ac .yui-ac-input {
1995 .ac .yui-ac-input {
1988 width:100%;
1996 width:100%;
1989 }
1997 }
1990
1998
1991 .ac .yui-ac-container {
1999 .ac .yui-ac-container {
1992 position:absolute;
2000 position:absolute;
1993 top:1.6em;
2001 top:1.6em;
1994 width:100%;
2002 width:100%;
1995 }
2003 }
1996
2004
1997 .ac .yui-ac-content {
2005 .ac .yui-ac-content {
1998 position:absolute;
2006 position:absolute;
1999 width:100%;
2007 width:100%;
2000 border:1px solid gray;
2008 border:1px solid gray;
2001 background:#fff;
2009 background:#fff;
2002 overflow:hidden;
2010 overflow:hidden;
2003 z-index:9050;
2011 z-index:9050;
2004 }
2012 }
2005
2013
2006 .ac .yui-ac-shadow {
2014 .ac .yui-ac-shadow {
2007 position:absolute;
2015 position:absolute;
2008 width:100%;
2016 width:100%;
2009 background:#000;
2017 background:#000;
2010 -moz-opacity:0.1px;
2018 -moz-opacity:0.1px;
2011 opacity:.10;
2019 opacity:.10;
2012 filter:alpha(opacity = 10);
2020 filter:alpha(opacity = 10);
2013 z-index:9049;
2021 z-index:9049;
2014 margin:.3em;
2022 margin:.3em;
2015 }
2023 }
2016
2024
2017 .ac .yui-ac-content ul {
2025 .ac .yui-ac-content ul {
2018 width:100%;
2026 width:100%;
2019 margin:0;
2027 margin:0;
2020 padding:0;
2028 padding:0;
2021 }
2029 }
2022
2030
2023 .ac .yui-ac-content li {
2031 .ac .yui-ac-content li {
2024 cursor:default;
2032 cursor:default;
2025 white-space:nowrap;
2033 white-space:nowrap;
2026 margin:0;
2034 margin:0;
2027 padding:2px 5px;
2035 padding:2px 5px;
2028 }
2036 }
2029
2037
2030 .ac .yui-ac-content li.yui-ac-prehighlight {
2038 .ac .yui-ac-content li.yui-ac-prehighlight {
2031 background:#B3D4FF;
2039 background:#B3D4FF;
2032 }
2040 }
2033
2041
2034 .ac .yui-ac-content li.yui-ac-highlight {
2042 .ac .yui-ac-content li.yui-ac-highlight {
2035 background:#556CB5;
2043 background:#556CB5;
2036 color:#FFF;
2044 color:#FFF;
2037 }
2045 }
2038
2046
2039
2047
2040 .follow{
2048 .follow{
2041 background:url("../images/icons/heart_add.png") no-repeat scroll 3px;
2049 background:url("../images/icons/heart_add.png") no-repeat scroll 3px;
2042 height: 16px;
2050 height: 16px;
2043 width: 20px;
2051 width: 20px;
2044 cursor: pointer;
2052 cursor: pointer;
2045 display: block;
2053 display: block;
2046 float: right;
2054 float: right;
2047 margin-top: 2px;
2055 margin-top: 2px;
2048 }
2056 }
2049
2057
2050 .following{
2058 .following{
2051 background:url("../images/icons/heart_delete.png") no-repeat scroll 3px;
2059 background:url("../images/icons/heart_delete.png") no-repeat scroll 3px;
2052 height: 16px;
2060 height: 16px;
2053 width: 20px;
2061 width: 20px;
2054 cursor: pointer;
2062 cursor: pointer;
2055 display: block;
2063 display: block;
2056 float: right;
2064 float: right;
2057 margin-top: 2px;
2065 margin-top: 2px;
2058 }
2066 }
2059
2067
2060 .currently_following{
2068 .currently_following{
2061 padding-left: 10px;
2069 padding-left: 10px;
2062 padding-bottom:5px;
2070 padding-bottom:5px;
2063 }
2071 }
2064
2072
2065 .add_icon {
2073 .add_icon {
2066 background:url("../images/icons/add.png") no-repeat scroll 3px;
2074 background:url("../images/icons/add.png") no-repeat scroll 3px;
2067 padding-left:20px;
2075 padding-left:20px;
2068 padding-top:0px;
2076 padding-top:0px;
2069 text-align:left;
2077 text-align:left;
2070 }
2078 }
2071
2079
2072 .edit_icon {
2080 .edit_icon {
2073 background:url("../images/icons/folder_edit.png") no-repeat scroll 3px;
2081 background:url("../images/icons/folder_edit.png") no-repeat scroll 3px;
2074 padding-left:20px;
2082 padding-left:20px;
2075 padding-top:0px;
2083 padding-top:0px;
2076 text-align:left;
2084 text-align:left;
2077 }
2085 }
2078
2086
2079 .delete_icon {
2087 .delete_icon {
2080 background:url("../images/icons/delete.png") no-repeat scroll 3px;
2088 background:url("../images/icons/delete.png") no-repeat scroll 3px;
2081 padding-left:20px;
2089 padding-left:20px;
2082 padding-top:0px;
2090 padding-top:0px;
2083 text-align:left;
2091 text-align:left;
2084 }
2092 }
2085
2093
2086 .refresh_icon {
2094 .refresh_icon {
2087 background:url("../images/icons/arrow_refresh.png") no-repeat scroll 3px;
2095 background:url("../images/icons/arrow_refresh.png") no-repeat scroll 3px;
2088 padding-left:20px;
2096 padding-left:20px;
2089 padding-top:0px;
2097 padding-top:0px;
2090 text-align:left;
2098 text-align:left;
2091 }
2099 }
2092
2100
2093 .pull_icon {
2101 .pull_icon {
2094 background:url("../images/icons/connect.png") no-repeat scroll 3px;
2102 background:url("../images/icons/connect.png") no-repeat scroll 3px;
2095 padding-left:20px;
2103 padding-left:20px;
2096 padding-top:0px;
2104 padding-top:0px;
2097 text-align:left;
2105 text-align:left;
2098 }
2106 }
2099
2107
2100 .rss_icon {
2108 .rss_icon {
2101 background:url("../images/icons/rss_16.png") no-repeat scroll 3px;
2109 background:url("../images/icons/rss_16.png") no-repeat scroll 3px;
2102 padding-left:20px;
2110 padding-left:20px;
2103 padding-top:0px;
2111 padding-top:0px;
2104 text-align:left;
2112 text-align:left;
2105 }
2113 }
2106
2114
2107 .atom_icon {
2115 .atom_icon {
2108 background:url("../images/icons/atom.png") no-repeat scroll 3px;
2116 background:url("../images/icons/atom.png") no-repeat scroll 3px;
2109 padding-left:20px;
2117 padding-left:20px;
2110 padding-top:0px;
2118 padding-top:0px;
2111 text-align:left;
2119 text-align:left;
2112 }
2120 }
2113
2121
2114 .archive_icon {
2122 .archive_icon {
2115 background:url("../images/icons/compress.png") no-repeat scroll 3px;
2123 background:url("../images/icons/compress.png") no-repeat scroll 3px;
2116 padding-left:20px;
2124 padding-left:20px;
2117 text-align:left;
2125 text-align:left;
2118 padding-top:1px;
2126 padding-top:1px;
2119 }
2127 }
2120
2128
2121 .start_following_icon {
2129 .start_following_icon {
2122 background:url("../images/icons/heart_add.png") no-repeat scroll 3px;
2130 background:url("../images/icons/heart_add.png") no-repeat scroll 3px;
2123 padding-left:20px;
2131 padding-left:20px;
2124 text-align:left;
2132 text-align:left;
2125 padding-top:0px;
2133 padding-top:0px;
2126 }
2134 }
2127
2135
2128 .stop_following_icon {
2136 .stop_following_icon {
2129 background:url("../images/icons/heart_delete.png") no-repeat scroll 3px;
2137 background:url("../images/icons/heart_delete.png") no-repeat scroll 3px;
2130 padding-left:20px;
2138 padding-left:20px;
2131 text-align:left;
2139 text-align:left;
2132 padding-top:0px;
2140 padding-top:0px;
2133 }
2141 }
2134
2142
2135 .action_button {
2143 .action_button {
2136 border:0;
2144 border:0;
2137 display:inline;
2145 display:inline;
2138 }
2146 }
2139
2147
2140 .action_button:hover {
2148 .action_button:hover {
2141 border:0;
2149 border:0;
2142 text-decoration:underline;
2150 text-decoration:underline;
2143 cursor:pointer;
2151 cursor:pointer;
2144 }
2152 }
2145
2153
2146 #switch_repos {
2154 #switch_repos {
2147 position:absolute;
2155 position:absolute;
2148 height:25px;
2156 height:25px;
2149 z-index:1;
2157 z-index:1;
2150 }
2158 }
2151
2159
2152 #switch_repos select {
2160 #switch_repos select {
2153 min-width:150px;
2161 min-width:150px;
2154 max-height:250px;
2162 max-height:250px;
2155 z-index:1;
2163 z-index:1;
2156 }
2164 }
2157
2165
2158 .breadcrumbs {
2166 .breadcrumbs {
2159 border:medium none;
2167 border:medium none;
2160 color:#FFF;
2168 color:#FFF;
2161 float:left;
2169 float:left;
2162 text-transform:uppercase;
2170 text-transform:uppercase;
2163 font-weight:700;
2171 font-weight:700;
2164 font-size:14px;
2172 font-size:14px;
2165 margin:0;
2173 margin:0;
2166 padding:11px 0 11px 10px;
2174 padding:11px 0 11px 10px;
2167 }
2175 }
2168
2176
2169 .breadcrumbs a {
2177 .breadcrumbs a {
2170 color:#FFF;
2178 color:#FFF;
2171 }
2179 }
2172
2180
2173 .flash_msg ul {
2181 .flash_msg ul {
2174 margin:0;
2182 margin:0;
2175 padding:0 0 10px;
2183 padding:0 0 10px;
2176 }
2184 }
2177
2185
2178 .error_msg {
2186 .error_msg {
2179 background-color:#FFCFCF;
2187 background-color:#FFCFCF;
2180 background-image:url("../images/icons/error_msg.png");
2188 background-image:url("../images/icons/error_msg.png");
2181 border:1px solid #FF9595;
2189 border:1px solid #FF9595;
2182 color:#C30;
2190 color:#C30;
2183 }
2191 }
2184
2192
2185 .warning_msg {
2193 .warning_msg {
2186 background-color:#FFFBCC;
2194 background-color:#FFFBCC;
2187 background-image:url("../images/icons/warning_msg.png");
2195 background-image:url("../images/icons/warning_msg.png");
2188 border:1px solid #FFF35E;
2196 border:1px solid #FFF35E;
2189 color:#C69E00;
2197 color:#C69E00;
2190 }
2198 }
2191
2199
2192 .success_msg {
2200 .success_msg {
2193 background-color:#D5FFCF;
2201 background-color:#D5FFCF;
2194 background-image:url("../images/icons/success_msg.png");
2202 background-image:url("../images/icons/success_msg.png");
2195 border:1px solid #97FF88;
2203 border:1px solid #97FF88;
2196 color:#090;
2204 color:#090;
2197 }
2205 }
2198
2206
2199 .notice_msg {
2207 .notice_msg {
2200 background-color:#DCE3FF;
2208 background-color:#DCE3FF;
2201 background-image:url("../images/icons/notice_msg.png");
2209 background-image:url("../images/icons/notice_msg.png");
2202 border:1px solid #93A8FF;
2210 border:1px solid #93A8FF;
2203 color:#556CB5;
2211 color:#556CB5;
2204 }
2212 }
2205
2213
2206 .success_msg,.error_msg,.notice_msg,.warning_msg {
2214 .success_msg,.error_msg,.notice_msg,.warning_msg {
2207 background-position:10px center;
2215 background-position:10px center;
2208 background-repeat:no-repeat;
2216 background-repeat:no-repeat;
2209 font-size:12px;
2217 font-size:12px;
2210 font-weight:700;
2218 font-weight:700;
2211 min-height:14px;
2219 min-height:14px;
2212 line-height:14px;
2220 line-height:14px;
2213 margin-bottom:0;
2221 margin-bottom:0;
2214 margin-top:0;
2222 margin-top:0;
2215 display:block;
2223 display:block;
2216 overflow:auto;
2224 overflow:auto;
2217 padding:6px 10px 6px 40px;
2225 padding:6px 10px 6px 40px;
2218 }
2226 }
2219
2227
2220 #msg_close {
2228 #msg_close {
2221 background:transparent url("../icons/cross_grey_small.png") no-repeat scroll 0 0;
2229 background:transparent url("../icons/cross_grey_small.png") no-repeat scroll 0 0;
2222 cursor:pointer;
2230 cursor:pointer;
2223 height:16px;
2231 height:16px;
2224 position:absolute;
2232 position:absolute;
2225 right:5px;
2233 right:5px;
2226 top:5px;
2234 top:5px;
2227 width:16px;
2235 width:16px;
2228 }
2236 }
2229
2237
2230 div#legend_container table,div#legend_choices table {
2238 div#legend_container table,div#legend_choices table {
2231 width:auto !important;
2239 width:auto !important;
2232 }
2240 }
2233
2241
2234 table#permissions_manage {
2242 table#permissions_manage {
2235 width:0 !important;
2243 width:0 !important;
2236 }
2244 }
2237
2245
2238 table#permissions_manage span.private_repo_msg {
2246 table#permissions_manage span.private_repo_msg {
2239 font-size:0.8em;
2247 font-size:0.8em;
2240 opacity:0.6px;
2248 opacity:0.6px;
2241 }
2249 }
2242
2250
2243 table#permissions_manage td.private_repo_msg {
2251 table#permissions_manage td.private_repo_msg {
2244 font-size:0.8em;
2252 font-size:0.8em;
2245 }
2253 }
2246
2254
2247 table#permissions_manage tr#add_perm_input td {
2255 table#permissions_manage tr#add_perm_input td {
2248 vertical-align:middle;
2256 vertical-align:middle;
2249 }
2257 }
2250
2258
2251 div.gravatar {
2259 div.gravatar {
2252 background-color:#FFF;
2260 background-color:#FFF;
2253 border:1px solid #D0D0D0;
2261 border:1px solid #D0D0D0;
2254 float:left;
2262 float:left;
2255 margin-right:0.7em;
2263 margin-right:0.7em;
2256 padding:2px 2px 0;
2264 padding:2px 2px 0;
2257 }
2265 }
2258
2266
2259 #header,#content,#footer {
2267 #header,#content,#footer {
2260 min-width:978px;
2268 min-width:978px;
2261 }
2269 }
2262
2270
2263 #content {
2271 #content {
2264 min-height:100%;
2272 min-height:100%;
2265 clear:both;
2273 clear:both;
2266 overflow:hidden;
2274 overflow:hidden;
2267 padding:14px 10px;
2275 padding:14px 10px;
2268 }
2276 }
2269
2277
2270 #content div.box div.title div.search {
2278 #content div.box div.title div.search {
2271 background:url("../images/title_link.png") no-repeat top left;
2279 background:url("../images/title_link.png") no-repeat top left;
2272 border-left:1px solid #316293;
2280 border-left:1px solid #316293;
2273 }
2281 }
2274
2282
2275 #content div.box div.title div.search div.input input {
2283 #content div.box div.title div.search div.input input {
2276 border:1px solid #316293;
2284 border:1px solid #316293;
2277 }
2285 }
2278
2286
2279 #content div.box div.title div.search div.button input.ui-button {
2287 #content div.box div.title div.search div.button input.ui-button {
2280 background:#4e85bb url("../images/button_highlight.png") repeat-x;
2288 background:#4e85bb url("../images/button_highlight.png") repeat-x;
2281 border:1px solid #316293;
2289 border:1px solid #316293;
2282 border-left:none;
2290 border-left:none;
2283 color:#FFF;
2291 color:#FFF;
2284 }
2292 }
2285
2293
2286 #content div.box input.ui-button-small {
2294 #content div.box input.ui-button-small {
2287 background:#e5e3e3 url("../images/button.png") repeat-x;
2295 background:#e5e3e3 url("../images/button.png") repeat-x;
2288 border-top:1px solid #DDD;
2296 border-top:1px solid #DDD;
2289 border-left:1px solid #c6c6c6;
2297 border-left:1px solid #c6c6c6;
2290 border-right:1px solid #DDD;
2298 border-right:1px solid #DDD;
2291 border-bottom:1px solid #c6c6c6;
2299 border-bottom:1px solid #c6c6c6;
2292 color:#515151;
2300 color:#515151;
2293 outline:none;
2301 outline:none;
2294 margin:0;
2302 margin:0;
2295 }
2303 }
2296
2304
2297 #content div.box input.ui-button-small-blue {
2305 #content div.box input.ui-button-small-blue {
2298 background:#4e85bb url("../images/button_highlight.png") repeat-x;
2306 background:#4e85bb url("../images/button_highlight.png") repeat-x;
2299 border-top:1px solid #5c91a4;
2307 border-top:1px solid #5c91a4;
2300 border-left:1px solid #2a6f89;
2308 border-left:1px solid #2a6f89;
2301 border-right:1px solid #2b7089;
2309 border-right:1px solid #2b7089;
2302 border-bottom:1px solid #1a6480;
2310 border-bottom:1px solid #1a6480;
2303 color:#fff;
2311 color:#fff;
2304 }
2312 }
2305
2313
2306 #content div.box input.ui-button-small submit,button{
2314 #content div.box input.ui-button-small submit,button{
2307 cursor: pointer;
2315 cursor: pointer;
2308 }
2316 }
2309
2317
2310 #content div.box div.title div.search div.button input.ui-state-hover {
2318 #content div.box div.title div.search div.button input.ui-state-hover {
2311 background:#46a0c1 url("../images/button_highlight_selected.png") repeat-x;
2319 background:#46a0c1 url("../images/button_highlight_selected.png") repeat-x;
2312 border:1px solid #316293;
2320 border:1px solid #316293;
2313 border-left:none;
2321 border-left:none;
2314 color:#FFF;
2322 color:#FFF;
2315 }
2323 }
2316
2324
2317 #content div.box div.form div.fields div.field div.highlight .ui-button {
2325 #content div.box div.form div.fields div.field div.highlight .ui-button {
2318 background:#4e85bb url("../images/button_highlight.png") repeat-x;
2326 background:#4e85bb url("../images/button_highlight.png") repeat-x;
2319 border-top:1px solid #5c91a4;
2327 border-top:1px solid #5c91a4;
2320 border-left:1px solid #2a6f89;
2328 border-left:1px solid #2a6f89;
2321 border-right:1px solid #2b7089;
2329 border-right:1px solid #2b7089;
2322 border-bottom:1px solid #1a6480;
2330 border-bottom:1px solid #1a6480;
2323 color:#fff;
2331 color:#fff;
2324 }
2332 }
2325
2333
2326 #content div.box div.form div.fields div.field div.highlight .ui-state-hover {
2334 #content div.box div.form div.fields div.field div.highlight .ui-state-hover {
2327 background:#46a0c1 url("../images/button_highlight_selected.png") repeat-x;
2335 background:#46a0c1 url("../images/button_highlight_selected.png") repeat-x;
2328 border-top:1px solid #78acbf;
2336 border-top:1px solid #78acbf;
2329 border-left:1px solid #34819e;
2337 border-left:1px solid #34819e;
2330 border-right:1px solid #35829f;
2338 border-right:1px solid #35829f;
2331 border-bottom:1px solid #257897;
2339 border-bottom:1px solid #257897;
2332 color:#fff;
2340 color:#fff;
2333 }
2341 }
2334
2342
2335 ins,div.options a:hover {
2343 ins,div.options a:hover {
2336 text-decoration:none;
2344 text-decoration:none;
2337 }
2345 }
2338
2346
2339 img,#header #header-inner #quick li a:hover span.normal,#header #header-inner #quick li ul li.last,#content div.box div.form div.fields div.field div.textarea table td table td a,#clone_url {
2347 img,#header #header-inner #quick li a:hover span.normal,#header #header-inner #quick li ul li.last,#content div.box div.form div.fields div.field div.textarea table td table td a,#clone_url {
2340 border:none;
2348 border:none;
2341 }
2349 }
2342
2350
2343 img.icon,.right .merge img {
2351 img.icon,.right .merge img {
2344 vertical-align:bottom;
2352 vertical-align:bottom;
2345 }
2353 }
2346
2354
2347 #header ul#logged-user,#content div.box div.title ul.links,#content div.box div.message div.dismiss,#content div.box div.traffic div.legend ul {
2355 #header ul#logged-user,#content div.box div.title ul.links,#content div.box div.message div.dismiss,#content div.box div.traffic div.legend ul {
2348 float:right;
2356 float:right;
2349 margin:0;
2357 margin:0;
2350 padding:0;
2358 padding:0;
2351 }
2359 }
2352
2360
2353 #header #header-inner #home,#header #header-inner #logo,#content div.box ul.left,#content div.box ol.left,#content div.box div.pagination-left,div#commit_history,div#legend_data,div#legend_container,div#legend_choices {
2361 #header #header-inner #home,#header #header-inner #logo,#content div.box ul.left,#content div.box ol.left,#content div.box div.pagination-left,div#commit_history,div#legend_data,div#legend_container,div#legend_choices {
2354 float:left;
2362 float:left;
2355 }
2363 }
2356
2364
2357 #header #header-inner #quick li:hover ul ul,#header #header-inner #quick li:hover ul ul ul,#header #header-inner #quick li:hover ul ul ul ul,#content #left #menu ul.closed,#content #left #menu li ul.collapsed,.yui-tt-shadow {
2365 #header #header-inner #quick li:hover ul ul,#header #header-inner #quick li:hover ul ul ul,#header #header-inner #quick li:hover ul ul ul ul,#content #left #menu ul.closed,#content #left #menu li ul.collapsed,.yui-tt-shadow {
2358 display:none;
2366 display:none;
2359 }
2367 }
2360
2368
2361 #header #header-inner #quick li:hover ul,#header #header-inner #quick li li:hover ul,#header #header-inner #quick li li li:hover ul,#header #header-inner #quick li li li li:hover ul,#content #left #menu ul.opened,#content #left #menu li ul.expanded {
2369 #header #header-inner #quick li:hover ul,#header #header-inner #quick li li:hover ul,#header #header-inner #quick li li li:hover ul,#header #header-inner #quick li li li li:hover ul,#content #left #menu ul.opened,#content #left #menu li ul.expanded {
2362 display:block;
2370 display:block;
2363 }
2371 }
2364
2372
2365 #content div.graph{
2373 #content div.graph{
2366 padding:0 10px 10px;
2374 padding:0 10px 10px;
2367 }
2375 }
2368
2376
2369 #content div.box div.title ul.links li a:hover,#content div.box div.title ul.links li.ui-tabs-selected a {
2377 #content div.box div.title ul.links li a:hover,#content div.box div.title ul.links li.ui-tabs-selected a {
2370 color:#bfe3ff;
2378 color:#bfe3ff;
2371 }
2379 }
2372
2380
2373 #content div.box ol.lower-roman,#content div.box ol.upper-roman,#content div.box ol.lower-alpha,#content div.box ol.upper-alpha,#content div.box ol.decimal {
2381 #content div.box ol.lower-roman,#content div.box ol.upper-roman,#content div.box ol.lower-alpha,#content div.box ol.upper-alpha,#content div.box ol.decimal {
2374 margin:10px 24px 10px 44px;
2382 margin:10px 24px 10px 44px;
2375 }
2383 }
2376
2384
2377 #content div.box div.form,#content div.box div.table,#content div.box div.traffic {
2385 #content div.box div.form,#content div.box div.table,#content div.box div.traffic {
2378 clear:both;
2386 clear:both;
2379 overflow:hidden;
2387 overflow:hidden;
2380 margin:0;
2388 margin:0;
2381 padding:0 20px 10px;
2389 padding:0 20px 10px;
2382 }
2390 }
2383
2391
2384 #content div.box div.form div.fields,#login div.form,#login div.form div.fields,#register div.form,#register div.form div.fields {
2392 #content div.box div.form div.fields,#login div.form,#login div.form div.fields,#register div.form,#register div.form div.fields {
2385 clear:both;
2393 clear:both;
2386 overflow:hidden;
2394 overflow:hidden;
2387 margin:0;
2395 margin:0;
2388 padding:0;
2396 padding:0;
2389 }
2397 }
2390
2398
2391 #content div.box div.form div.fields div.field div.label span,#login div.form div.fields div.field div.label span,#register div.form div.fields div.field div.label span {
2399 #content div.box div.form div.fields div.field div.label span,#login div.form div.fields div.field div.label span,#register div.form div.fields div.field div.label span {
2392 height:1%;
2400 height:1%;
2393 display:block;
2401 display:block;
2394 color:#363636;
2402 color:#363636;
2395 margin:0;
2403 margin:0;
2396 padding:2px 0 0;
2404 padding:2px 0 0;
2397 }
2405 }
2398
2406
2399 #content div.box div.form div.fields div.field div.input input.error,#login div.form div.fields div.field div.input input.error,#register div.form div.fields div.field div.input input.error {
2407 #content div.box div.form div.fields div.field div.input input.error,#login div.form div.fields div.field div.input input.error,#register div.form div.fields div.field div.input input.error {
2400 background:#FBE3E4;
2408 background:#FBE3E4;
2401 border-top:1px solid #e1b2b3;
2409 border-top:1px solid #e1b2b3;
2402 border-left:1px solid #e1b2b3;
2410 border-left:1px solid #e1b2b3;
2403 border-right:1px solid #FBC2C4;
2411 border-right:1px solid #FBC2C4;
2404 border-bottom:1px solid #FBC2C4;
2412 border-bottom:1px solid #FBC2C4;
2405 }
2413 }
2406
2414
2407 #content div.box div.form div.fields div.field div.input input.success,#login div.form div.fields div.field div.input input.success,#register div.form div.fields div.field div.input input.success {
2415 #content div.box div.form div.fields div.field div.input input.success,#login div.form div.fields div.field div.input input.success,#register div.form div.fields div.field div.input input.success {
2408 background:#E6EFC2;
2416 background:#E6EFC2;
2409 border-top:1px solid #cebb98;
2417 border-top:1px solid #cebb98;
2410 border-left:1px solid #cebb98;
2418 border-left:1px solid #cebb98;
2411 border-right:1px solid #c6d880;
2419 border-right:1px solid #c6d880;
2412 border-bottom:1px solid #c6d880;
2420 border-bottom:1px solid #c6d880;
2413 }
2421 }
2414
2422
2415 #content div.box-left div.form div.fields div.field div.textarea,#content div.box-right div.form div.fields div.field div.textarea,#content div.box div.form div.fields div.field div.select select,#content div.box table th.selected input,#content div.box table td.selected input {
2423 #content div.box-left div.form div.fields div.field div.textarea,#content div.box-right div.form div.fields div.field div.textarea,#content div.box div.form div.fields div.field div.select select,#content div.box table th.selected input,#content div.box table td.selected input {
2416 margin:0;
2424 margin:0;
2417 }
2425 }
2418
2426
2419 #content div.box-left div.form div.fields div.field div.select,#content div.box-left div.form div.fields div.field div.checkboxes,#content div.box-left div.form div.fields div.field div.radios,#content div.box-right div.form div.fields div.field div.select,#content div.box-right div.form div.fields div.field div.checkboxes,#content div.box-right div.form div.fields div.field div.radios{
2427 #content div.box-left div.form div.fields div.field div.select,#content div.box-left div.form div.fields div.field div.checkboxes,#content div.box-left div.form div.fields div.field div.radios,#content div.box-right div.form div.fields div.field div.select,#content div.box-right div.form div.fields div.field div.checkboxes,#content div.box-right div.form div.fields div.field div.radios{
2420 margin:0 0 0 0px !important;
2428 margin:0 0 0 0px !important;
2421 padding:0;
2429 padding:0;
2422 }
2430 }
2423
2431
2424 #content div.box div.form div.fields div.field div.select,#content div.box div.form div.fields div.field div.checkboxes,#content div.box div.form div.fields div.field div.radios {
2432 #content div.box div.form div.fields div.field div.select,#content div.box div.form div.fields div.field div.checkboxes,#content div.box div.form div.fields div.field div.radios {
2425 margin:0 0 0 200px;
2433 margin:0 0 0 200px;
2426 padding:0;
2434 padding:0;
2427 }
2435 }
2428
2436
2429
2437
2430 #content div.box div.form div.fields div.field div.select a:hover,#content div.box div.form div.fields div.field div.select a.ui-selectmenu:hover,#content div.box div.action a:hover {
2438 #content div.box div.form div.fields div.field div.select a:hover,#content div.box div.form div.fields div.field div.select a.ui-selectmenu:hover,#content div.box div.action a:hover {
2431 color:#000;
2439 color:#000;
2432 text-decoration:none;
2440 text-decoration:none;
2433 }
2441 }
2434
2442
2435 #content div.box div.form div.fields div.field div.select a.ui-selectmenu-focus,#content div.box div.action a.ui-selectmenu-focus {
2443 #content div.box div.form div.fields div.field div.select a.ui-selectmenu-focus,#content div.box div.action a.ui-selectmenu-focus {
2436 border:1px solid #666;
2444 border:1px solid #666;
2437 }
2445 }
2438
2446
2439 #content div.box div.form div.fields div.field div.checkboxes div.checkbox,#content div.box div.form div.fields div.field div.radios div.radio {
2447 #content div.box div.form div.fields div.field div.checkboxes div.checkbox,#content div.box div.form div.fields div.field div.radios div.radio {
2440 clear:both;
2448 clear:both;
2441 overflow:hidden;
2449 overflow:hidden;
2442 margin:0;
2450 margin:0;
2443 padding:8px 0 2px;
2451 padding:8px 0 2px;
2444 }
2452 }
2445
2453
2446 #content div.box div.form div.fields div.field div.checkboxes div.checkbox input,#content div.box div.form div.fields div.field div.radios div.radio input {
2454 #content div.box div.form div.fields div.field div.checkboxes div.checkbox input,#content div.box div.form div.fields div.field div.radios div.radio input {
2447 float:left;
2455 float:left;
2448 margin:0;
2456 margin:0;
2449 }
2457 }
2450
2458
2451 #content div.box div.form div.fields div.field div.checkboxes div.checkbox label,#content div.box div.form div.fields div.field div.radios div.radio label {
2459 #content div.box div.form div.fields div.field div.checkboxes div.checkbox label,#content div.box div.form div.fields div.field div.radios div.radio label {
2452 height:1%;
2460 height:1%;
2453 display:block;
2461 display:block;
2454 float:left;
2462 float:left;
2455 margin:2px 0 0 4px;
2463 margin:2px 0 0 4px;
2456 }
2464 }
2457
2465
2458 div.form div.fields div.field div.button input,#content div.box div.form div.fields div.buttons input,div.form div.fields div.buttons input,#content div.box div.action div.button input {
2466 div.form div.fields div.field div.button input,#content div.box div.form div.fields div.buttons input,div.form div.fields div.buttons input,#content div.box div.action div.button input {
2459 color:#000;
2467 color:#000;
2460 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
2468 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
2461 font-size:11px;
2469 font-size:11px;
2462 font-weight:700;
2470 font-weight:700;
2463 margin:0;
2471 margin:0;
2464 }
2472 }
2465
2473
2466 div.form div.fields div.field div.button .ui-button,#content div.box div.form div.fields div.buttons input.ui-button {
2474 div.form div.fields div.field div.button .ui-button,#content div.box div.form div.fields div.buttons input.ui-button {
2467 background:#e5e3e3 url("../images/button.png") repeat-x;
2475 background:#e5e3e3 url("../images/button.png") repeat-x;
2468 border-top:1px solid #DDD;
2476 border-top:1px solid #DDD;
2469 border-left:1px solid #c6c6c6;
2477 border-left:1px solid #c6c6c6;
2470 border-right:1px solid #DDD;
2478 border-right:1px solid #DDD;
2471 border-bottom:1px solid #c6c6c6;
2479 border-bottom:1px solid #c6c6c6;
2472 color:#515151;
2480 color:#515151;
2473 outline:none;
2481 outline:none;
2474 margin:0;
2482 margin:0;
2475 padding:6px 12px;
2483 padding:6px 12px;
2476 }
2484 }
2477
2485
2478 div.form div.fields div.field div.button .ui-state-hover,#content div.box div.form div.fields div.buttons input.ui-state-hover {
2486 div.form div.fields div.field div.button .ui-state-hover,#content div.box div.form div.fields div.buttons input.ui-state-hover {
2479 background:#b4b4b4 url("../images/button_selected.png") repeat-x;
2487 background:#b4b4b4 url("../images/button_selected.png") repeat-x;
2480 border-top:1px solid #ccc;
2488 border-top:1px solid #ccc;
2481 border-left:1px solid #bebebe;
2489 border-left:1px solid #bebebe;
2482 border-right:1px solid #b1b1b1;
2490 border-right:1px solid #b1b1b1;
2483 border-bottom:1px solid #afafaf;
2491 border-bottom:1px solid #afafaf;
2484 color:#515151;
2492 color:#515151;
2485 outline:none;
2493 outline:none;
2486 margin:0;
2494 margin:0;
2487 padding:6px 12px;
2495 padding:6px 12px;
2488 }
2496 }
2489
2497
2490 div.form div.fields div.field div.highlight,#content div.box div.form div.fields div.buttons div.highlight {
2498 div.form div.fields div.field div.highlight,#content div.box div.form div.fields div.buttons div.highlight {
2491 display:inline;
2499 display:inline;
2492 }
2500 }
2493
2501
2494 #content div.box div.form div.fields div.buttons,div.form div.fields div.buttons {
2502 #content div.box div.form div.fields div.buttons,div.form div.fields div.buttons {
2495 margin:10px 0 0 200px;
2503 margin:10px 0 0 200px;
2496 padding:0;
2504 padding:0;
2497 }
2505 }
2498
2506
2499 #content div.box-left div.form div.fields div.buttons,#content div.box-right div.form div.fields div.buttons,div.box-left div.form div.fields div.buttons,div.box-right div.form div.fields div.buttons {
2507 #content div.box-left div.form div.fields div.buttons,#content div.box-right div.form div.fields div.buttons,div.box-left div.form div.fields div.buttons,div.box-right div.form div.fields div.buttons {
2500 margin:10px 0 0;
2508 margin:10px 0 0;
2501 }
2509 }
2502
2510
2503 #content div.box table td.user,#content div.box table td.address {
2511 #content div.box table td.user,#content div.box table td.address {
2504 width:10%;
2512 width:10%;
2505 text-align:center;
2513 text-align:center;
2506 }
2514 }
2507
2515
2508 #content div.box div.action div.button,#login div.form div.fields div.field div.input div.link,#register div.form div.fields div.field div.input div.link {
2516 #content div.box div.action div.button,#login div.form div.fields div.field div.input div.link,#register div.form div.fields div.field div.input div.link {
2509 text-align:right;
2517 text-align:right;
2510 margin:6px 0 0;
2518 margin:6px 0 0;
2511 padding:0;
2519 padding:0;
2512 }
2520 }
2513
2521
2514 #content div.box div.action div.button input.ui-button,#login div.form div.fields div.buttons input.ui-button,#register div.form div.fields div.buttons input.ui-button {
2522 #content div.box div.action div.button input.ui-button,#login div.form div.fields div.buttons input.ui-button,#register div.form div.fields div.buttons input.ui-button {
2515 background:#e5e3e3 url("../images/button.png") repeat-x;
2523 background:#e5e3e3 url("../images/button.png") repeat-x;
2516 border-top:1px solid #DDD;
2524 border-top:1px solid #DDD;
2517 border-left:1px solid #c6c6c6;
2525 border-left:1px solid #c6c6c6;
2518 border-right:1px solid #DDD;
2526 border-right:1px solid #DDD;
2519 border-bottom:1px solid #c6c6c6;
2527 border-bottom:1px solid #c6c6c6;
2520 color:#515151;
2528 color:#515151;
2521 margin:0;
2529 margin:0;
2522 padding:6px 12px;
2530 padding:6px 12px;
2523 }
2531 }
2524
2532
2525 #content div.box div.action div.button input.ui-state-hover,#login div.form div.fields div.buttons input.ui-state-hover,#register div.form div.fields div.buttons input.ui-state-hover {
2533 #content div.box div.action div.button input.ui-state-hover,#login div.form div.fields div.buttons input.ui-state-hover,#register div.form div.fields div.buttons input.ui-state-hover {
2526 background:#b4b4b4 url("../images/button_selected.png") repeat-x;
2534 background:#b4b4b4 url("../images/button_selected.png") repeat-x;
2527 border-top:1px solid #ccc;
2535 border-top:1px solid #ccc;
2528 border-left:1px solid #bebebe;
2536 border-left:1px solid #bebebe;
2529 border-right:1px solid #b1b1b1;
2537 border-right:1px solid #b1b1b1;
2530 border-bottom:1px solid #afafaf;
2538 border-bottom:1px solid #afafaf;
2531 color:#515151;
2539 color:#515151;
2532 margin:0;
2540 margin:0;
2533 padding:6px 12px;
2541 padding:6px 12px;
2534 }
2542 }
2535
2543
2536 #content div.box div.pagination div.results,#content div.box div.pagination-wh div.results {
2544 #content div.box div.pagination div.results,#content div.box div.pagination-wh div.results {
2537 text-align:left;
2545 text-align:left;
2538 float:left;
2546 float:left;
2539 margin:0;
2547 margin:0;
2540 padding:0;
2548 padding:0;
2541 }
2549 }
2542
2550
2543 #content div.box div.pagination div.results span,#content div.box div.pagination-wh div.results span {
2551 #content div.box div.pagination div.results span,#content div.box div.pagination-wh div.results span {
2544 height:1%;
2552 height:1%;
2545 display:block;
2553 display:block;
2546 float:left;
2554 float:left;
2547 background:#ebebeb url("../images/pager.png") repeat-x;
2555 background:#ebebeb url("../images/pager.png") repeat-x;
2548 border-top:1px solid #dedede;
2556 border-top:1px solid #dedede;
2549 border-left:1px solid #cfcfcf;
2557 border-left:1px solid #cfcfcf;
2550 border-right:1px solid #c4c4c4;
2558 border-right:1px solid #c4c4c4;
2551 border-bottom:1px solid #c4c4c4;
2559 border-bottom:1px solid #c4c4c4;
2552 color:#4A4A4A;
2560 color:#4A4A4A;
2553 font-weight:700;
2561 font-weight:700;
2554 margin:0;
2562 margin:0;
2555 padding:6px 8px;
2563 padding:6px 8px;
2556 }
2564 }
2557
2565
2558 #content div.box div.pagination ul.pager li.disabled,#content div.box div.pagination-wh a.disabled {
2566 #content div.box div.pagination ul.pager li.disabled,#content div.box div.pagination-wh a.disabled {
2559 color:#B4B4B4;
2567 color:#B4B4B4;
2560 padding:6px;
2568 padding:6px;
2561 }
2569 }
2562
2570
2563 #login,#register {
2571 #login,#register {
2564 width:520px;
2572 width:520px;
2565 margin:10% auto 0;
2573 margin:10% auto 0;
2566 padding:0;
2574 padding:0;
2567 }
2575 }
2568
2576
2569 #login div.color,#register div.color {
2577 #login div.color,#register div.color {
2570 clear:both;
2578 clear:both;
2571 overflow:hidden;
2579 overflow:hidden;
2572 background:#FFF;
2580 background:#FFF;
2573 margin:10px auto 0;
2581 margin:10px auto 0;
2574 padding:3px 3px 3px 0;
2582 padding:3px 3px 3px 0;
2575 }
2583 }
2576
2584
2577 #login div.color a,#register div.color a {
2585 #login div.color a,#register div.color a {
2578 width:20px;
2586 width:20px;
2579 height:20px;
2587 height:20px;
2580 display:block;
2588 display:block;
2581 float:left;
2589 float:left;
2582 margin:0 0 0 3px;
2590 margin:0 0 0 3px;
2583 padding:0;
2591 padding:0;
2584 }
2592 }
2585
2593
2586 #login div.title h5,#register div.title h5 {
2594 #login div.title h5,#register div.title h5 {
2587 color:#fff;
2595 color:#fff;
2588 margin:10px;
2596 margin:10px;
2589 padding:0;
2597 padding:0;
2590 }
2598 }
2591
2599
2592 #login div.form div.fields div.field,#register div.form div.fields div.field {
2600 #login div.form div.fields div.field,#register div.form div.fields div.field {
2593 clear:both;
2601 clear:both;
2594 overflow:hidden;
2602 overflow:hidden;
2595 margin:0;
2603 margin:0;
2596 padding:0 0 10px;
2604 padding:0 0 10px;
2597 }
2605 }
2598
2606
2599 #login div.form div.fields div.field span.error-message,#register div.form div.fields div.field span.error-message {
2607 #login div.form div.fields div.field span.error-message,#register div.form div.fields div.field span.error-message {
2600 height:1%;
2608 height:1%;
2601 display:block;
2609 display:block;
2602 color:red;
2610 color:red;
2603 margin:8px 0 0;
2611 margin:8px 0 0;
2604 padding:0;
2612 padding:0;
2605 max-width: 320px;
2613 max-width: 320px;
2606 }
2614 }
2607
2615
2608 #login div.form div.fields div.field div.label label,#register div.form div.fields div.field div.label label {
2616 #login div.form div.fields div.field div.label label,#register div.form div.fields div.field div.label label {
2609 color:#000;
2617 color:#000;
2610 font-weight:700;
2618 font-weight:700;
2611 }
2619 }
2612
2620
2613 #login div.form div.fields div.field div.input,#register div.form div.fields div.field div.input {
2621 #login div.form div.fields div.field div.input,#register div.form div.fields div.field div.input {
2614 float:left;
2622 float:left;
2615 margin:0;
2623 margin:0;
2616 padding:0;
2624 padding:0;
2617 }
2625 }
2618
2626
2619 #login div.form div.fields div.field div.checkbox,#register div.form div.fields div.field div.checkbox {
2627 #login div.form div.fields div.field div.checkbox,#register div.form div.fields div.field div.checkbox {
2620 margin:0 0 0 184px;
2628 margin:0 0 0 184px;
2621 padding:0;
2629 padding:0;
2622 }
2630 }
2623
2631
2624 #login div.form div.fields div.field div.checkbox label,#register div.form div.fields div.field div.checkbox label {
2632 #login div.form div.fields div.field div.checkbox label,#register div.form div.fields div.field div.checkbox label {
2625 color:#565656;
2633 color:#565656;
2626 font-weight:700;
2634 font-weight:700;
2627 }
2635 }
2628
2636
2629 #login div.form div.fields div.buttons input,#register div.form div.fields div.buttons input {
2637 #login div.form div.fields div.buttons input,#register div.form div.fields div.buttons input {
2630 color:#000;
2638 color:#000;
2631 font-size:1em;
2639 font-size:1em;
2632 font-weight:700;
2640 font-weight:700;
2633 font-family:Verdana, Helvetica, Sans-Serif;
2641 font-family:Verdana, Helvetica, Sans-Serif;
2634 margin:0;
2642 margin:0;
2635 }
2643 }
2636
2644
2637 #changeset_content .container .wrapper,#graph_content .container .wrapper {
2645 #changeset_content .container .wrapper,#graph_content .container .wrapper {
2638 width:600px;
2646 width:600px;
2639 }
2647 }
2640
2648
2641 #changeset_content .container .left,#graph_content .container .left {
2649 #changeset_content .container .left,#graph_content .container .left {
2642 float:left;
2650 float:left;
2643 width:70%;
2651 width:70%;
2644 padding-left:5px;
2652 padding-left:5px;
2645 }
2653 }
2646
2654
2647 #changeset_content .container .left .date,.ac .match {
2655 #changeset_content .container .left .date,.ac .match {
2648 font-weight:700;
2656 font-weight:700;
2649 padding-top: 5px;
2657 padding-top: 5px;
2650 padding-bottom:5px;
2658 padding-bottom:5px;
2651 }
2659 }
2652
2660
2653 div#legend_container table td,div#legend_choices table td {
2661 div#legend_container table td,div#legend_choices table td {
2654 border:none !important;
2662 border:none !important;
2655 height:20px !important;
2663 height:20px !important;
2656 padding:0 !important;
2664 padding:0 !important;
2657 }
2665 }
2658
2666
2659 #q_filter{
2667 #q_filter{
2660 border:0 none;
2668 border:0 none;
2661 color:#AAAAAA;
2669 color:#AAAAAA;
2662 margin-bottom:-4px;
2670 margin-bottom:-4px;
2663 margin-top:-4px;
2671 margin-top:-4px;
2664 padding-left:3px;
2672 padding-left:3px;
2665 }
2673 }
2666
2674
@@ -1,401 +1,401 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="root.html"/>
2 <%inherit file="root.html"/>
3
3
4 <!-- HEADER -->
4 <!-- HEADER -->
5 <div id="header">
5 <div id="header">
6 <!-- user -->
6 <!-- user -->
7 <ul id="logged-user">
7 <ul id="logged-user">
8 <li class="first">
8 <li class="first">
9
9
10 <div id="quick_login" style="display:none">
10 <div id="quick_login" style="display:none">
11 ${h.form(h.url('login_home',came_from=h.url.current()))}
11 ${h.form(h.url('login_home',came_from=h.url.current()))}
12 <div class="form">
12 <div class="form">
13 <div class="fields">
13 <div class="fields">
14 <div class="field">
14 <div class="field">
15 <div class="label">
15 <div class="label">
16 <label for="username">${_('Username')}:</label>
16 <label for="username">${_('Username')}:</label>
17 </div>
17 </div>
18 <div class="input">
18 <div class="input">
19 ${h.text('username',class_='focus',size=40)}
19 ${h.text('username',class_='focus',size=40)}
20 </div>
20 </div>
21
21
22 </div>
22 </div>
23 <div class="field">
23 <div class="field">
24 <div class="label">
24 <div class="label">
25 <label for="password">${_('Password')}:</label>
25 <label for="password">${_('Password')}:</label>
26 </div>
26 </div>
27 <div class="input">
27 <div class="input">
28 ${h.password('password',class_='focus',size=40)}
28 ${h.password('password',class_='focus',size=40)}
29 </div>
29 </div>
30
30
31 </div>
31 </div>
32 <div class="buttons">
32 <div class="buttons">
33 ${h.submit('sign_in','Sign In',class_="ui-button")}
33 <div class="password_forgoten">${h.link_to(_('Forgot password ?'),h.url('reset_password'))}</div>${h.submit('sign_in','Sign In',class_="ui-button")}
34 </div>
34 </div>
35 </div>
35 </div>
36 </div>
36 </div>
37 ${h.end_form()}
37 ${h.end_form()}
38 <script type="text/javascript">
38 <script type="text/javascript">
39 YUE.on('quick_login_link','click',function(e){
39 YUE.on('quick_login_link','click',function(e){
40
40
41 if(YUD.hasClass('quick_login_link','enabled')){
41 if(YUD.hasClass('quick_login_link','enabled')){
42 YUD.setStyle('quick_login','display','none');
42 YUD.setStyle('quick_login','display','none');
43 YUD.removeClass('quick_login_link','enabled');
43 YUD.removeClass('quick_login_link','enabled');
44 }
44 }
45 else{
45 else{
46 YUD.setStyle('quick_login','display','');
46 YUD.setStyle('quick_login','display','');
47 YUD.addClass('quick_login_link','enabled');
47 YUD.addClass('quick_login_link','enabled');
48 YUD.get('username').focus();
48 YUD.get('username').focus();
49 }
49 }
50 //make sure we don't redirect
50 //make sure we don't redirect
51 YUE.preventDefault(e);
51 YUE.preventDefault(e);
52 });
52 });
53
53
54 </script>
54 </script>
55 </div>
55 </div>
56
56
57 <div class="gravatar">
57 <div class="gravatar">
58 <img alt="gravatar" src="${h.gravatar_url(c.rhodecode_user.email,20)}" />
58 <img alt="gravatar" src="${h.gravatar_url(c.rhodecode_user.email,20)}" />
59 </div>
59 </div>
60 <div class="account">
60 <div class="account">
61 %if c.rhodecode_user.username == 'default':
61 %if c.rhodecode_user.username == 'default':
62 <a href="${h.url('public_journal')}">${_('Public journal')}</a>
62 <a href="${h.url('public_journal')}">${_('Public journal')}</a>
63 %else:
63 %else:
64 ${h.link_to(c.rhodecode_user.username,h.url('admin_settings_my_account'),title='%s %s'%(c.rhodecode_user.name,c.rhodecode_user.lastname))}
64 ${h.link_to(c.rhodecode_user.username,h.url('admin_settings_my_account'),title='%s %s'%(c.rhodecode_user.name,c.rhodecode_user.lastname))}
65 %endif
65 %endif
66 </div>
66 </div>
67 </li>
67 </li>
68 <li>
68 <li>
69 <a href="${h.url('home')}">${_('Home')}</a>
69 <a href="${h.url('home')}">${_('Home')}</a>
70 </li>
70 </li>
71 %if c.rhodecode_user.username != 'default':
71 %if c.rhodecode_user.username != 'default':
72 <li>
72 <li>
73 <a href="${h.url('journal')}">${_('Journal')}</a>
73 <a href="${h.url('journal')}">${_('Journal')}</a>
74 ##(${c.unread_journal}
74 ##(${c.unread_journal}
75 </li>
75 </li>
76 %endif
76 %endif
77 %if c.rhodecode_user.username == 'default':
77 %if c.rhodecode_user.username == 'default':
78 <li class="last highlight">${h.link_to(u'Login',h.url('login_home'),id='quick_login_link')}</li>
78 <li class="last highlight">${h.link_to(u'Login',h.url('login_home'),id='quick_login_link')}</li>
79 %else:
79 %else:
80 <li class="last highlight">${h.link_to(u'Log Out',h.url('logout_home'))}</li>
80 <li class="last highlight">${h.link_to(u'Log Out',h.url('logout_home'))}</li>
81 %endif
81 %endif
82 </ul>
82 </ul>
83 <!-- end user -->
83 <!-- end user -->
84 <div id="header-inner" class="title top-left-rounded-corner top-right-rounded-corner">
84 <div id="header-inner" class="title top-left-rounded-corner top-right-rounded-corner">
85 <div id="logo">
85 <div id="logo">
86 <h1><a href="${h.url('home')}">${c.rhodecode_name}</a></h1>
86 <h1><a href="${h.url('home')}">${c.rhodecode_name}</a></h1>
87 </div>
87 </div>
88 <!-- MENU -->
88 <!-- MENU -->
89 ${self.page_nav()}
89 ${self.page_nav()}
90 <!-- END MENU -->
90 <!-- END MENU -->
91 ${self.body()}
91 ${self.body()}
92 </div>
92 </div>
93 </div>
93 </div>
94 <!-- END HEADER -->
94 <!-- END HEADER -->
95
95
96 <!-- CONTENT -->
96 <!-- CONTENT -->
97 <div id="content">
97 <div id="content">
98 <div class="flash_msg">
98 <div class="flash_msg">
99 <% messages = h.flash.pop_messages() %>
99 <% messages = h.flash.pop_messages() %>
100 % if messages:
100 % if messages:
101 <ul id="flash-messages">
101 <ul id="flash-messages">
102 % for message in messages:
102 % for message in messages:
103 <li class="${message.category}_msg">${message}</li>
103 <li class="${message.category}_msg">${message}</li>
104 % endfor
104 % endfor
105 </ul>
105 </ul>
106 % endif
106 % endif
107 </div>
107 </div>
108 <div id="main">
108 <div id="main">
109 ${next.main()}
109 ${next.main()}
110 </div>
110 </div>
111 </div>
111 </div>
112 <!-- END CONTENT -->
112 <!-- END CONTENT -->
113
113
114 <!-- FOOTER -->
114 <!-- FOOTER -->
115 <div id="footer">
115 <div id="footer">
116 <div id="footer-inner" class="title bottom-left-rounded-corner bottom-right-rounded-corner">
116 <div id="footer-inner" class="title bottom-left-rounded-corner bottom-right-rounded-corner">
117 <div>
117 <div>
118 <p class="footer-link">
118 <p class="footer-link">
119 <a href="${h.url('bugtracker')}">${_('Submit a bug')}</a>
119 <a href="${h.url('bugtracker')}">${_('Submit a bug')}</a>
120 </p>
120 </p>
121 <p class="footer-link-right">
121 <p class="footer-link-right">
122 <a href="${h.url('rhodecode_official')}">RhodeCode</a>
122 <a href="${h.url('rhodecode_official')}">RhodeCode</a>
123 ${c.rhodecode_version} &copy; 2010-${h.datetime.today().year} by Marcin Kuzminski
123 ${c.rhodecode_version} &copy; 2010-${h.datetime.today().year} by Marcin Kuzminski
124 </p>
124 </p>
125 </div>
125 </div>
126 </div>
126 </div>
127 <script type="text/javascript">
127 <script type="text/javascript">
128 function tooltip_activate(){
128 function tooltip_activate(){
129 ${h.tooltip.activate()}
129 ${h.tooltip.activate()}
130 }
130 }
131 tooltip_activate();
131 tooltip_activate();
132 </script>
132 </script>
133 </div>
133 </div>
134 <!-- END FOOTER -->
134 <!-- END FOOTER -->
135
135
136 ### MAKO DEFS ###
136 ### MAKO DEFS ###
137 <%def name="page_nav()">
137 <%def name="page_nav()">
138 ${self.menu()}
138 ${self.menu()}
139 </%def>
139 </%def>
140
140
141 <%def name="breadcrumbs()">
141 <%def name="breadcrumbs()">
142 <div class="breadcrumbs">
142 <div class="breadcrumbs">
143 ${self.breadcrumbs_links()}
143 ${self.breadcrumbs_links()}
144 </div>
144 </div>
145 </%def>
145 </%def>
146
146
147
147
148 <%def name="menu(current=None)">
148 <%def name="menu(current=None)">
149 <%
149 <%
150 def is_current(selected):
150 def is_current(selected):
151 if selected == current:
151 if selected == current:
152 return h.literal('class="current"')
152 return h.literal('class="current"')
153 %>
153 %>
154 %if current not in ['home','admin']:
154 %if current not in ['home','admin']:
155 ##REGULAR MENU
155 ##REGULAR MENU
156 <ul id="quick">
156 <ul id="quick">
157 <!-- repo switcher -->
157 <!-- repo switcher -->
158 <li>
158 <li>
159 <a id="repo_switcher" title="${_('Switch repository')}" href="#">
159 <a id="repo_switcher" title="${_('Switch repository')}" href="#">
160 <span class="icon">
160 <span class="icon">
161 <img src="${h.url('/images/icons/database.png')}" alt="${_('Products')}" />
161 <img src="${h.url('/images/icons/database.png')}" alt="${_('Products')}" />
162 </span>
162 </span>
163 <span>&darr;</span>
163 <span>&darr;</span>
164 </a>
164 </a>
165 <ul id="repo_switcher_list" class="repo_switcher">
165 <ul id="repo_switcher_list" class="repo_switcher">
166 <li>
166 <li>
167 <a href="#">${_('loading...')}</a>
167 <a href="#">${_('loading...')}</a>
168 </li>
168 </li>
169 </ul>
169 </ul>
170 <script type="text/javascript">
170 <script type="text/javascript">
171 YUE.on('repo_switcher','mouseover',function(){
171 YUE.on('repo_switcher','mouseover',function(){
172 function qfilter(){
172 function qfilter(){
173 var S = YAHOO.util.Selector;
173 var S = YAHOO.util.Selector;
174
174
175 var q_filter = YUD.get('q_filter_rs');
175 var q_filter = YUD.get('q_filter_rs');
176 var F = YAHOO.namespace('q_filter_rs');
176 var F = YAHOO.namespace('q_filter_rs');
177
177
178 YUE.on(q_filter,'click',function(){
178 YUE.on(q_filter,'click',function(){
179 q_filter.value = '';
179 q_filter.value = '';
180 });
180 });
181
181
182 F.filterTimeout = null;
182 F.filterTimeout = null;
183
183
184 F.updateFilter = function() {
184 F.updateFilter = function() {
185 // Reset timeout
185 // Reset timeout
186 F.filterTimeout = null;
186 F.filterTimeout = null;
187
187
188 var obsolete = [];
188 var obsolete = [];
189 var nodes = S.query('ul#repo_switcher_list li a.repo_name');
189 var nodes = S.query('ul#repo_switcher_list li a.repo_name');
190 var req = YUD.get('q_filter_rs').value;
190 var req = YUD.get('q_filter_rs').value;
191 for (n in nodes){
191 for (n in nodes){
192 YUD.setStyle(nodes[n].parentNode,'display','')
192 YUD.setStyle(nodes[n].parentNode,'display','')
193 }
193 }
194 if (req){
194 if (req){
195 for (n in nodes){
195 for (n in nodes){
196 console.log(n);
196 console.log(n);
197 if (nodes[n].innerHTML.toLowerCase().indexOf(req) == -1) {
197 if (nodes[n].innerHTML.toLowerCase().indexOf(req) == -1) {
198 obsolete.push(nodes[n]);
198 obsolete.push(nodes[n]);
199 }
199 }
200 }
200 }
201 if(obsolete){
201 if(obsolete){
202 for (n in obsolete){
202 for (n in obsolete){
203 YUD.setStyle(obsolete[n].parentNode,'display','none');
203 YUD.setStyle(obsolete[n].parentNode,'display','none');
204 }
204 }
205 }
205 }
206 }
206 }
207 }
207 }
208
208
209 YUE.on(q_filter,'keyup',function(e){
209 YUE.on(q_filter,'keyup',function(e){
210 clearTimeout(F.filterTimeout);
210 clearTimeout(F.filterTimeout);
211 setTimeout(F.updateFilter,600);
211 setTimeout(F.updateFilter,600);
212 });
212 });
213 }
213 }
214 var loaded = YUD.hasClass('repo_switcher','loaded');
214 var loaded = YUD.hasClass('repo_switcher','loaded');
215 if(!loaded){
215 if(!loaded){
216 YUD.addClass('repo_switcher','loaded');
216 YUD.addClass('repo_switcher','loaded');
217 YAHOO.util.Connect.asyncRequest('GET',"${h.url('repo_switcher')}",{
217 YAHOO.util.Connect.asyncRequest('GET',"${h.url('repo_switcher')}",{
218 success:function(o){
218 success:function(o){
219 YUD.get('repo_switcher_list').innerHTML = o.responseText;
219 YUD.get('repo_switcher_list').innerHTML = o.responseText;
220 qfilter();
220 qfilter();
221 },
221 },
222 failure:function(o){
222 failure:function(o){
223 YUD.removeClass('repo_switcher','loaded');
223 YUD.removeClass('repo_switcher','loaded');
224 }
224 }
225 },null);
225 },null);
226 }
226 }
227 return false;
227 return false;
228 });
228 });
229 </script>
229 </script>
230 </li>
230 </li>
231
231
232 <li ${is_current('summary')}>
232 <li ${is_current('summary')}>
233 <a title="${_('Summary')}" href="${h.url('summary_home',repo_name=c.repo_name)}">
233 <a title="${_('Summary')}" href="${h.url('summary_home',repo_name=c.repo_name)}">
234 <span class="icon">
234 <span class="icon">
235 <img src="${h.url('/images/icons/clipboard_16.png')}" alt="${_('Summary')}" />
235 <img src="${h.url('/images/icons/clipboard_16.png')}" alt="${_('Summary')}" />
236 </span>
236 </span>
237 <span>${_('Summary')}</span>
237 <span>${_('Summary')}</span>
238 </a>
238 </a>
239 </li>
239 </li>
240 ##<li ${is_current('shortlog')}>
240 ##<li ${is_current('shortlog')}>
241 ## <a title="${_('Shortlog')}" href="${h.url('shortlog_home',repo_name=c.repo_name)}">
241 ## <a title="${_('Shortlog')}" href="${h.url('shortlog_home',repo_name=c.repo_name)}">
242 ## <span class="icon">
242 ## <span class="icon">
243 ## <img src="${h.url("/images/icons/application_view_list.png")}" alt="${_('Shortlog')}" />
243 ## <img src="${h.url("/images/icons/application_view_list.png")}" alt="${_('Shortlog')}" />
244 ## </span>
244 ## </span>
245 ## <span>${_('Shortlog')}</span>
245 ## <span>${_('Shortlog')}</span>
246 ## </a>
246 ## </a>
247 ##</li>
247 ##</li>
248 <li ${is_current('changelog')}>
248 <li ${is_current('changelog')}>
249 <a title="${_('Changelog')}" href="${h.url('changelog_home',repo_name=c.repo_name)}">
249 <a title="${_('Changelog')}" href="${h.url('changelog_home',repo_name=c.repo_name)}">
250 <span class="icon">
250 <span class="icon">
251 <img src="${h.url('/images/icons/time.png')}" alt="${_('Changelog')}" />
251 <img src="${h.url('/images/icons/time.png')}" alt="${_('Changelog')}" />
252 </span>
252 </span>
253 <span>${_('Changelog')}</span>
253 <span>${_('Changelog')}</span>
254 </a>
254 </a>
255 </li>
255 </li>
256
256
257 <li ${is_current('switch_to')}>
257 <li ${is_current('switch_to')}>
258 <a title="${_('Switch to')}" href="#">
258 <a title="${_('Switch to')}" href="#">
259 <span class="icon">
259 <span class="icon">
260 <img src="${h.url('/images/icons/arrow_switch.png')}" alt="${_('Switch to')}" />
260 <img src="${h.url('/images/icons/arrow_switch.png')}" alt="${_('Switch to')}" />
261 </span>
261 </span>
262 <span>${_('Switch to')}</span>
262 <span>${_('Switch to')}</span>
263 </a>
263 </a>
264 <ul>
264 <ul>
265 <li>
265 <li>
266 ${h.link_to('%s (%s)' % (_('branches'),len(c.rhodecode_repo.branches.values()),),h.url('branches_home',repo_name=c.repo_name),class_='branches childs')}
266 ${h.link_to('%s (%s)' % (_('branches'),len(c.rhodecode_repo.branches.values()),),h.url('branches_home',repo_name=c.repo_name),class_='branches childs')}
267 <ul>
267 <ul>
268 %if c.rhodecode_repo.branches.values():
268 %if c.rhodecode_repo.branches.values():
269 %for cnt,branch in enumerate(c.rhodecode_repo.branches.items()):
269 %for cnt,branch in enumerate(c.rhodecode_repo.branches.items()):
270 <li>${h.link_to('%s - %s' % (branch[0],h.short_id(branch[1])),h.url('files_home',repo_name=c.repo_name,revision=branch[1]))}</li>
270 <li>${h.link_to('%s - %s' % (branch[0],h.short_id(branch[1])),h.url('files_home',repo_name=c.repo_name,revision=branch[1]))}</li>
271 %endfor
271 %endfor
272 %else:
272 %else:
273 <li>${h.link_to(_('There are no branches yet'),'#')}</li>
273 <li>${h.link_to(_('There are no branches yet'),'#')}</li>
274 %endif
274 %endif
275 </ul>
275 </ul>
276 </li>
276 </li>
277 <li>
277 <li>
278 ${h.link_to('%s (%s)' % (_('tags'),len(c.rhodecode_repo.tags.values()),),h.url('tags_home',repo_name=c.repo_name),class_='tags childs')}
278 ${h.link_to('%s (%s)' % (_('tags'),len(c.rhodecode_repo.tags.values()),),h.url('tags_home',repo_name=c.repo_name),class_='tags childs')}
279 <ul>
279 <ul>
280 %if c.rhodecode_repo.tags.values():
280 %if c.rhodecode_repo.tags.values():
281 %for cnt,tag in enumerate(c.rhodecode_repo.tags.items()):
281 %for cnt,tag in enumerate(c.rhodecode_repo.tags.items()):
282 <li>${h.link_to('%s - %s' % (tag[0],h.short_id(tag[1])),h.url('files_home',repo_name=c.repo_name,revision=tag[1]))}</li>
282 <li>${h.link_to('%s - %s' % (tag[0],h.short_id(tag[1])),h.url('files_home',repo_name=c.repo_name,revision=tag[1]))}</li>
283 %endfor
283 %endfor
284 %else:
284 %else:
285 <li>${h.link_to(_('There are no tags yet'),'#')}</li>
285 <li>${h.link_to(_('There are no tags yet'),'#')}</li>
286 %endif
286 %endif
287 </ul>
287 </ul>
288 </li>
288 </li>
289 </ul>
289 </ul>
290 </li>
290 </li>
291 <li ${is_current('files')}>
291 <li ${is_current('files')}>
292 <a title="${_('Files')}" href="${h.url('files_home',repo_name=c.repo_name)}">
292 <a title="${_('Files')}" href="${h.url('files_home',repo_name=c.repo_name)}">
293 <span class="icon">
293 <span class="icon">
294 <img src="${h.url('/images/icons/file.png')}" alt="${_('Files')}" />
294 <img src="${h.url('/images/icons/file.png')}" alt="${_('Files')}" />
295 </span>
295 </span>
296 <span>${_('Files')}</span>
296 <span>${_('Files')}</span>
297 </a>
297 </a>
298 </li>
298 </li>
299
299
300 <li ${is_current('options')}>
300 <li ${is_current('options')}>
301 <a title="${_('Options')}" href="#">
301 <a title="${_('Options')}" href="#">
302 <span class="icon">
302 <span class="icon">
303 <img src="${h.url('/images/icons/table_gear.png')}" alt="${_('Admin')}" />
303 <img src="${h.url('/images/icons/table_gear.png')}" alt="${_('Admin')}" />
304 </span>
304 </span>
305 <span>${_('Options')}</span>
305 <span>${_('Options')}</span>
306 </a>
306 </a>
307 <ul>
307 <ul>
308 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
308 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
309 %if h.HasPermissionAll('hg.admin')('access settings on repository'):
309 %if h.HasPermissionAll('hg.admin')('access settings on repository'):
310 <li>${h.link_to(_('settings'),h.url('edit_repo',repo_name=c.repo_name),class_='settings')}</li>
310 <li>${h.link_to(_('settings'),h.url('edit_repo',repo_name=c.repo_name),class_='settings')}</li>
311 %else:
311 %else:
312 <li>${h.link_to(_('settings'),h.url('repo_settings_home',repo_name=c.repo_name),class_='settings')}</li>
312 <li>${h.link_to(_('settings'),h.url('repo_settings_home',repo_name=c.repo_name),class_='settings')}</li>
313 %endif
313 %endif
314 %endif
314 %endif
315 <li>${h.link_to(_('fork'),h.url('repo_fork_home',repo_name=c.repo_name),class_='fork')}</li>
315 <li>${h.link_to(_('fork'),h.url('repo_fork_home',repo_name=c.repo_name),class_='fork')}</li>
316 <li>${h.link_to(_('search'),h.url('search_repo',search_repo=c.repo_name),class_='search')}</li>
316 <li>${h.link_to(_('search'),h.url('search_repo',search_repo=c.repo_name),class_='search')}</li>
317
317
318 % if h.HasPermissionAll('hg.admin')('access admin main page'):
318 % if h.HasPermissionAll('hg.admin')('access admin main page'):
319 <li>
319 <li>
320 ${h.link_to(_('admin'),h.url('admin_home'),class_='admin')}
320 ${h.link_to(_('admin'),h.url('admin_home'),class_='admin')}
321 <%def name="admin_menu()">
321 <%def name="admin_menu()">
322 <ul>
322 <ul>
323 <li>${h.link_to(_('journal'),h.url('admin_home'),class_='journal')}</li>
323 <li>${h.link_to(_('journal'),h.url('admin_home'),class_='journal')}</li>
324 <li>${h.link_to(_('repositories'),h.url('repos'),class_='repos')}</li>
324 <li>${h.link_to(_('repositories'),h.url('repos'),class_='repos')}</li>
325 <li>${h.link_to(_('repositories groups'),h.url('repos_groups'),class_='repos_groups')}</li>
325 <li>${h.link_to(_('repositories groups'),h.url('repos_groups'),class_='repos_groups')}</li>
326 <li>${h.link_to(_('users'),h.url('users'),class_='users')}</li>
326 <li>${h.link_to(_('users'),h.url('users'),class_='users')}</li>
327 <li>${h.link_to(_('users groups'),h.url('users_groups'),class_='groups')}</li>
327 <li>${h.link_to(_('users groups'),h.url('users_groups'),class_='groups')}</li>
328 <li>${h.link_to(_('permissions'),h.url('edit_permission',id='default'),class_='permissions')}</li>
328 <li>${h.link_to(_('permissions'),h.url('edit_permission',id='default'),class_='permissions')}</li>
329 <li>${h.link_to(_('ldap'),h.url('ldap_home'),class_='ldap')}</li>
329 <li>${h.link_to(_('ldap'),h.url('ldap_home'),class_='ldap')}</li>
330 <li class="last">${h.link_to(_('settings'),h.url('admin_settings'),class_='settings')}</li>
330 <li class="last">${h.link_to(_('settings'),h.url('admin_settings'),class_='settings')}</li>
331 </ul>
331 </ul>
332 </%def>
332 </%def>
333
333
334 ${admin_menu()}
334 ${admin_menu()}
335 </li>
335 </li>
336 % endif
336 % endif
337 </ul>
337 </ul>
338 </li>
338 </li>
339
339
340 <li>
340 <li>
341 <a title="${_('Followers')}" href="${h.url('repo_followers_home',repo_name=c.repo_name)}">
341 <a title="${_('Followers')}" href="${h.url('repo_followers_home',repo_name=c.repo_name)}">
342 <span class="icon_short">
342 <span class="icon_short">
343 <img src="${h.url('/images/icons/heart.png')}" alt="${_('Followers')}" />
343 <img src="${h.url('/images/icons/heart.png')}" alt="${_('Followers')}" />
344 </span>
344 </span>
345 <span id="current_followers_count" class="short">${c.repository_followers}</span>
345 <span id="current_followers_count" class="short">${c.repository_followers}</span>
346 </a>
346 </a>
347 </li>
347 </li>
348 <li>
348 <li>
349 <a title="${_('Forks')}" href="${h.url('repo_forks_home',repo_name=c.repo_name)}">
349 <a title="${_('Forks')}" href="${h.url('repo_forks_home',repo_name=c.repo_name)}">
350 <span class="icon_short">
350 <span class="icon_short">
351 <img src="${h.url('/images/icons/arrow_divide.png')}" alt="${_('Forks')}" />
351 <img src="${h.url('/images/icons/arrow_divide.png')}" alt="${_('Forks')}" />
352 </span>
352 </span>
353 <span class="short">${c.repository_forks}</span>
353 <span class="short">${c.repository_forks}</span>
354 </a>
354 </a>
355 </li>
355 </li>
356
356
357 </ul>
357 </ul>
358 %else:
358 %else:
359 ##ROOT MENU
359 ##ROOT MENU
360 <ul id="quick">
360 <ul id="quick">
361 <li>
361 <li>
362 <a title="${_('Home')}" href="${h.url('home')}">
362 <a title="${_('Home')}" href="${h.url('home')}">
363 <span class="icon">
363 <span class="icon">
364 <img src="${h.url('/images/icons/home_16.png')}" alt="${_('Home')}" />
364 <img src="${h.url('/images/icons/home_16.png')}" alt="${_('Home')}" />
365 </span>
365 </span>
366 <span>${_('Home')}</span>
366 <span>${_('Home')}</span>
367 </a>
367 </a>
368 </li>
368 </li>
369 % if c.rhodecode_user.username != 'default':
369 % if c.rhodecode_user.username != 'default':
370 <li>
370 <li>
371 <a title="${_('Journal')}" href="${h.url('journal')}">
371 <a title="${_('Journal')}" href="${h.url('journal')}">
372 <span class="icon">
372 <span class="icon">
373 <img src="${h.url('/images/icons/book.png')}" alt="${_('Journal')}" />
373 <img src="${h.url('/images/icons/book.png')}" alt="${_('Journal')}" />
374 </span>
374 </span>
375 <span>${_('Journal')}</span>
375 <span>${_('Journal')}</span>
376 </a>
376 </a>
377 </li>
377 </li>
378 % endif
378 % endif
379 <li>
379 <li>
380 <a title="${_('Search')}" href="${h.url('search')}">
380 <a title="${_('Search')}" href="${h.url('search')}">
381 <span class="icon">
381 <span class="icon">
382 <img src="${h.url('/images/icons/search_16.png')}" alt="${_('Search')}" />
382 <img src="${h.url('/images/icons/search_16.png')}" alt="${_('Search')}" />
383 </span>
383 </span>
384 <span>${_('Search')}</span>
384 <span>${_('Search')}</span>
385 </a>
385 </a>
386 </li>
386 </li>
387
387
388 %if h.HasPermissionAll('hg.admin')('access admin main page'):
388 %if h.HasPermissionAll('hg.admin')('access admin main page'):
389 <li ${is_current('admin')}>
389 <li ${is_current('admin')}>
390 <a title="${_('Admin')}" href="${h.url('admin_home')}">
390 <a title="${_('Admin')}" href="${h.url('admin_home')}">
391 <span class="icon">
391 <span class="icon">
392 <img src="${h.url('/images/icons/cog_edit.png')}" alt="${_('Admin')}" />
392 <img src="${h.url('/images/icons/cog_edit.png')}" alt="${_('Admin')}" />
393 </span>
393 </span>
394 <span>${_('Admin')}</span>
394 <span>${_('Admin')}</span>
395 </a>
395 </a>
396 ${admin_menu()}
396 ${admin_menu()}
397 </li>
397 </li>
398 %endif
398 %endif
399 </ul>
399 </ul>
400 %endif
400 %endif
401 </%def> No newline at end of file
401 </%def>
@@ -1,44 +1,44 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="base/root.html"/>
2 <%inherit file="base/root.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Reset You password')} - ${c.rhodecode_name}
5 ${_('Reset You password')} - ${c.rhodecode_name}
6 </%def>
6 </%def>
7
7
8 <div id="register">
8 <div id="register">
9
9
10 <div class="title top-left-rounded-corner top-right-rounded-corner">
10 <div class="title top-left-rounded-corner top-right-rounded-corner">
11 <h5>${_('Reset You password to')} ${c.rhodecode_name}</h5>
11 <h5>${_('Reset You password to')} ${c.rhodecode_name}</h5>
12 </div>
12 </div>
13 <div class="inner">
13 <div class="inner">
14 ${h.form(url('password_reset'))}
14 ${h.form(url('password_reset'))}
15 <div class="form">
15 <div class="form">
16 <!-- fields -->
16 <!-- fields -->
17 <div class="fields">
17 <div class="fields">
18
18
19 <div class="field">
19 <div class="field">
20 <div class="label">
20 <div class="label">
21 <label for="email">${_('Email address')}:</label>
21 <label for="email">${_('Email address')}:</label>
22 </div>
22 </div>
23 <div class="input">
23 <div class="input">
24 ${h.text('email')}
24 ${h.text('email')}
25 </div>
25 </div>
26 </div>
26 </div>
27
27
28 <div class="buttons">
28 <div class="buttons">
29 <div class="nohighlight">
29 <div class="nohighlight">
30 ${h.submit('send','Reset my password',class_="ui-button")}
30 ${h.submit('send','Reset my password',class_="ui-button")}
31 <div class="activation_msg">${_('Your new password will be send to matching email address')}</div>
31 <div class="activation_msg">${_('Password reset link will be send to matching email address')}</div>
32 </div>
32 </div>
33 </div>
33 </div>
34 </div>
34 </div>
35 </div>
35 </div>
36 ${h.end_form()}
36 ${h.end_form()}
37 <script type="text/javascript">
37 <script type="text/javascript">
38 YUE.onDOMReady(function(){
38 YUE.onDOMReady(function(){
39 YUD.get('email').focus();
39 YUD.get('email').focus();
40 })
40 })
41 </script>
41 </script>
42 </div>
42 </div>
43 </div>
43 </div>
44
44
@@ -1,230 +1,261 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 from rhodecode.tests import *
2 from rhodecode.tests import *
3 from rhodecode.model.db import User
3 from rhodecode.model.db import User
4 from rhodecode.lib import generate_api_key
4 from rhodecode.lib.auth import check_password
5 from rhodecode.lib.auth import check_password
5
6
6
7
7 class TestLoginController(TestController):
8 class TestLoginController(TestController):
8
9
9 def test_index(self):
10 def test_index(self):
10 response = self.app.get(url(controller='login', action='index'))
11 response = self.app.get(url(controller='login', action='index'))
11 assert response.status == '200 OK', 'Wrong response from login page got %s' % response.status
12 self.assertEqual(response.status, '200 OK')
12 # Test response...
13 # Test response...
13
14
14 def test_login_admin_ok(self):
15 def test_login_admin_ok(self):
15 response = self.app.post(url(controller='login', action='index'),
16 response = self.app.post(url(controller='login', action='index'),
16 {'username':'test_admin',
17 {'username':'test_admin',
17 'password':'test12'})
18 'password':'test12'})
18 assert response.status == '302 Found', 'Wrong response code from login got %s' % response.status
19 self.assertEqual(response.status, '302 Found')
19 assert response.session['rhodecode_user'].username == 'test_admin', 'wrong logged in user'
20 self.assertEqual(response.session['rhodecode_user'].username ,
21 'test_admin')
20 response = response.follow()
22 response = response.follow()
21 assert '%s repository' % HG_REPO in response.body
23 self.assertTrue('%s repository' % HG_REPO in response.body)
22
24
23 def test_login_regular_ok(self):
25 def test_login_regular_ok(self):
24 response = self.app.post(url(controller='login', action='index'),
26 response = self.app.post(url(controller='login', action='index'),
25 {'username':'test_regular',
27 {'username':'test_regular',
26 'password':'test12'})
28 'password':'test12'})
27 print response
29
28 assert response.status == '302 Found', 'Wrong response code from login got %s' % response.status
30 self.assertEqual(response.status, '302 Found')
29 assert response.session['rhodecode_user'].username == 'test_regular', 'wrong logged in user'
31 self.assertEqual(response.session['rhodecode_user'].username ,
32 'test_regular')
30 response = response.follow()
33 response = response.follow()
31 assert '%s repository' % HG_REPO in response.body
34 self.assertTrue('%s repository' % HG_REPO in response.body)
32 assert '<a title="Admin" href="/_admin">' not in response.body
35 self.assertTrue('<a title="Admin" href="/_admin">' not in response.body)
33
36
34 def test_login_ok_came_from(self):
37 def test_login_ok_came_from(self):
35 test_came_from = '/_admin/users'
38 test_came_from = '/_admin/users'
36 response = self.app.post(url(controller='login', action='index', came_from=test_came_from),
39 response = self.app.post(url(controller='login', action='index',
40 came_from=test_came_from),
37 {'username':'test_admin',
41 {'username':'test_admin',
38 'password':'test12'})
42 'password':'test12'})
39 assert response.status == '302 Found', 'Wrong response code from came from redirection'
43 self.assertEqual(response.status, '302 Found')
40 response = response.follow()
44 response = response.follow()
41
45
42 assert response.status == '200 OK', 'Wrong response from login page got %s' % response.status
46 self.assertEqual(response.status, '200 OK')
43 assert 'Users administration' in response.body, 'No proper title in response'
47 self.assertTrue('Users administration' in response.body)
44
48
45
49
46 def test_login_short_password(self):
50 def test_login_short_password(self):
47 response = self.app.post(url(controller='login', action='index'),
51 response = self.app.post(url(controller='login', action='index'),
48 {'username':'test_admin',
52 {'username':'test_admin',
49 'password':'as'})
53 'password':'as'})
50 self.assertEqual(response.status, '200 OK')
54 self.assertEqual(response.status, '200 OK')
51 print response.body
55
52 self.assertTrue('Enter 3 characters or more' in response.body)
56 self.assertTrue('Enter 3 characters or more' in response.body)
53
57
54 def test_login_wrong_username_password(self):
58 def test_login_wrong_username_password(self):
55 response = self.app.post(url(controller='login', action='index'),
59 response = self.app.post(url(controller='login', action='index'),
56 {'username':'error',
60 {'username':'error',
57 'password':'test12'})
61 'password':'test12'})
58 assert response.status == '200 OK', 'Wrong response from login page'
62 self.assertEqual(response.status , '200 OK')
59
63
60 assert 'invalid user name' in response.body, 'No error username message in response'
64 self.assertTrue('invalid user name' in response.body)
61 assert 'invalid password' in response.body, 'No error password message in response'
65 self.assertTrue('invalid password' in response.body)
62
66
63 #==========================================================================
67 #==========================================================================
64 # REGISTRATIONS
68 # REGISTRATIONS
65 #==========================================================================
69 #==========================================================================
66 def test_register(self):
70 def test_register(self):
67 response = self.app.get(url(controller='login', action='register'))
71 response = self.app.get(url(controller='login', action='register'))
68 assert 'Sign Up to RhodeCode' in response.body, 'wrong page for user registration'
72 self.assertTrue('Sign Up to RhodeCode' in response.body)
69
73
70 def test_register_err_same_username(self):
74 def test_register_err_same_username(self):
71 response = self.app.post(url(controller='login', action='register'),
75 response = self.app.post(url(controller='login', action='register'),
72 {'username':'test_admin',
76 {'username':'test_admin',
73 'password':'test12',
77 'password':'test12',
74 'password_confirmation':'test12',
78 'password_confirmation':'test12',
75 'email':'goodmail@domain.com',
79 'email':'goodmail@domain.com',
76 'name':'test',
80 'name':'test',
77 'lastname':'test'})
81 'lastname':'test'})
78
82
79 assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
83 self.assertEqual(response.status , '200 OK')
80 assert 'This username already exists' in response.body
84 self.assertTrue('This username already exists' in response.body)
81
85
82 def test_register_err_same_email(self):
86 def test_register_err_same_email(self):
83 response = self.app.post(url(controller='login', action='register'),
87 response = self.app.post(url(controller='login', action='register'),
84 {'username':'test_admin_0',
88 {'username':'test_admin_0',
85 'password':'test12',
89 'password':'test12',
86 'password_confirmation':'test12',
90 'password_confirmation':'test12',
87 'email':'test_admin@mail.com',
91 'email':'test_admin@mail.com',
88 'name':'test',
92 'name':'test',
89 'lastname':'test'})
93 'lastname':'test'})
90
94
91 assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
95 self.assertEqual(response.status , '200 OK')
92 assert 'This e-mail address is already taken' in response.body
96 assert 'This e-mail address is already taken' in response.body
93
97
94 def test_register_err_same_email_case_sensitive(self):
98 def test_register_err_same_email_case_sensitive(self):
95 response = self.app.post(url(controller='login', action='register'),
99 response = self.app.post(url(controller='login', action='register'),
96 {'username':'test_admin_1',
100 {'username':'test_admin_1',
97 'password':'test12',
101 'password':'test12',
98 'password_confirmation':'test12',
102 'password_confirmation':'test12',
99 'email':'TesT_Admin@mail.COM',
103 'email':'TesT_Admin@mail.COM',
100 'name':'test',
104 'name':'test',
101 'lastname':'test'})
105 'lastname':'test'})
102 assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
106 self.assertEqual(response.status , '200 OK')
103 assert 'This e-mail address is already taken' in response.body
107 assert 'This e-mail address is already taken' in response.body
104
108
105 def test_register_err_wrong_data(self):
109 def test_register_err_wrong_data(self):
106 response = self.app.post(url(controller='login', action='register'),
110 response = self.app.post(url(controller='login', action='register'),
107 {'username':'xs',
111 {'username':'xs',
108 'password':'test',
112 'password':'test',
109 'password_confirmation':'test',
113 'password_confirmation':'test',
110 'email':'goodmailm',
114 'email':'goodmailm',
111 'name':'test',
115 'name':'test',
112 'lastname':'test'})
116 'lastname':'test'})
113 assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
117 self.assertEqual(response.status , '200 OK')
114 assert 'An email address must contain a single @' in response.body
118 assert 'An email address must contain a single @' in response.body
115 assert 'Enter a value 6 characters long or more' in response.body
119 assert 'Enter a value 6 characters long or more' in response.body
116
120
117
121
118 def test_register_err_username(self):
122 def test_register_err_username(self):
119 response = self.app.post(url(controller='login', action='register'),
123 response = self.app.post(url(controller='login', action='register'),
120 {'username':'error user',
124 {'username':'error user',
121 'password':'test12',
125 'password':'test12',
122 'password_confirmation':'test12',
126 'password_confirmation':'test12',
123 'email':'goodmailm',
127 'email':'goodmailm',
124 'name':'test',
128 'name':'test',
125 'lastname':'test'})
129 'lastname':'test'})
126
130
127 print response.body
131 self.assertEqual(response.status , '200 OK')
128 assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
129 assert 'An email address must contain a single @' in response.body
132 assert 'An email address must contain a single @' in response.body
130 assert ('Username may only contain '
133 assert ('Username may only contain '
131 'alphanumeric characters underscores, '
134 'alphanumeric characters underscores, '
132 'periods or dashes and must begin with '
135 'periods or dashes and must begin with '
133 'alphanumeric character') in response.body
136 'alphanumeric character') in response.body
134
137
135 def test_register_err_case_sensitive(self):
138 def test_register_err_case_sensitive(self):
136 response = self.app.post(url(controller='login', action='register'),
139 response = self.app.post(url(controller='login', action='register'),
137 {'username':'Test_Admin',
140 {'username':'Test_Admin',
138 'password':'test12',
141 'password':'test12',
139 'password_confirmation':'test12',
142 'password_confirmation':'test12',
140 'email':'goodmailm',
143 'email':'goodmailm',
141 'name':'test',
144 'name':'test',
142 'lastname':'test'})
145 'lastname':'test'})
143
146
144 assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
147 self.assertEqual(response.status , '200 OK')
145 assert 'An email address must contain a single @' in response.body
148 assert 'An email address must contain a single @' in response.body
146 assert 'This username already exists' in response.body
149 assert 'This username already exists' in response.body
147
150
148
151
149
152
150 def test_register_special_chars(self):
153 def test_register_special_chars(self):
151 response = self.app.post(url(controller='login', action='register'),
154 response = self.app.post(url(controller='login', action='register'),
152 {'username':'xxxaxn',
155 {'username':'xxxaxn',
153 'password':'Δ…Δ‡ΕΊΕΌΔ…Ε›Ε›Ε›Ε›',
156 'password':'Δ…Δ‡ΕΊΕΌΔ…Ε›Ε›Ε›Ε›',
154 'password_confirmation':'Δ…Δ‡ΕΊΕΌΔ…Ε›Ε›Ε›Ε›',
157 'password_confirmation':'Δ…Δ‡ΕΊΕΌΔ…Ε›Ε›Ε›Ε›',
155 'email':'goodmailm@test.plx',
158 'email':'goodmailm@test.plx',
156 'name':'test',
159 'name':'test',
157 'lastname':'test'})
160 'lastname':'test'})
158
161
159 print response.body
162 self.assertEqual(response.status , '200 OK')
160 assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
161 assert 'Invalid characters in password' in response.body
163 assert 'Invalid characters in password' in response.body
162
164
163
165
164 def test_register_password_mismatch(self):
166 def test_register_password_mismatch(self):
165 response = self.app.post(url(controller='login', action='register'),
167 response = self.app.post(url(controller='login', action='register'),
166 {'username':'xs',
168 {'username':'xs',
167 'password':'123qwe',
169 'password':'123qwe',
168 'password_confirmation':'qwe123',
170 'password_confirmation':'qwe123',
169 'email':'goodmailm@test.plxa',
171 'email':'goodmailm@test.plxa',
170 'name':'test',
172 'name':'test',
171 'lastname':'test'})
173 'lastname':'test'})
172
174
173 assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
175 self.assertEqual(response.status , '200 OK')
174 print response.body
175 assert 'Password do not match' in response.body
176 assert 'Password do not match' in response.body
176
177
177 def test_register_ok(self):
178 def test_register_ok(self):
178 username = 'test_regular4'
179 username = 'test_regular4'
179 password = 'qweqwe'
180 password = 'qweqwe'
180 email = 'marcin@test.com'
181 email = 'marcin@test.com'
181 name = 'testname'
182 name = 'testname'
182 lastname = 'testlastname'
183 lastname = 'testlastname'
183
184
184 response = self.app.post(url(controller='login', action='register'),
185 response = self.app.post(url(controller='login', action='register'),
185 {'username':username,
186 {'username':username,
186 'password':password,
187 'password':password,
187 'password_confirmation':password,
188 'password_confirmation':password,
188 'email':email,
189 'email':email,
189 'name':name,
190 'name':name,
190 'lastname':lastname})
191 'lastname':lastname})
191 assert response.status == '302 Found', 'Wrong response from register page got %s' % response.status
192 self.assertEqual(response.status , '302 Found')
192 assert 'You have successfully registered into rhodecode' in response.session['flash'][0], 'No flash message about user registration'
193 assert 'You have successfully registered into rhodecode' in response.session['flash'][0], 'No flash message about user registration'
193
194
194 ret = self.sa.query(User).filter(User.username == 'test_regular4').one()
195 ret = self.sa.query(User).filter(User.username == 'test_regular4').one()
195 assert ret.username == username , 'field mismatch %s %s' % (ret.username, username)
196 assert ret.username == username , 'field mismatch %s %s' % (ret.username, username)
196 assert check_password(password, ret.password) == True , 'password mismatch'
197 assert check_password(password, ret.password) == True , 'password mismatch'
197 assert ret.email == email , 'field mismatch %s %s' % (ret.email, email)
198 assert ret.email == email , 'field mismatch %s %s' % (ret.email, email)
198 assert ret.name == name , 'field mismatch %s %s' % (ret.name, name)
199 assert ret.name == name , 'field mismatch %s %s' % (ret.name, name)
199 assert ret.lastname == lastname , 'field mismatch %s %s' % (ret.lastname, lastname)
200 assert ret.lastname == lastname , 'field mismatch %s %s' % (ret.lastname, lastname)
200
201
201
202
202 def test_forgot_password_wrong_mail(self):
203 def test_forgot_password_wrong_mail(self):
203 response = self.app.post(url(controller='login', action='password_reset'),
204 response = self.app.post(url(controller='login', action='password_reset'),
204 {'email':'marcin@wrongmail.org', })
205 {'email':'marcin@wrongmail.org', })
205
206
206 assert "This e-mail address doesn't exist" in response.body, 'Missing error message about wrong email'
207 assert "This e-mail address doesn't exist" in response.body, 'Missing error message about wrong email'
207
208
208 def test_forgot_password(self):
209 def test_forgot_password(self):
209 response = self.app.get(url(controller='login', action='password_reset'))
210 response = self.app.get(url(controller='login',
210 assert response.status == '200 OK', 'Wrong response from login page got %s' % response.status
211 action='password_reset'))
212 self.assertEqual(response.status , '200 OK')
211
213
212 username = 'test_password_reset_1'
214 username = 'test_password_reset_1'
213 password = 'qweqwe'
215 password = 'qweqwe'
214 email = 'marcin@python-works.com'
216 email = 'marcin@python-works.com'
215 name = 'passwd'
217 name = 'passwd'
216 lastname = 'reset'
218 lastname = 'reset'
217
219
218 response = self.app.post(url(controller='login', action='register'),
220 new = User()
219 {'username':username,
221 new.username = username
220 'password':password,
222 new.password = password
221 'password_confirmation':password,
223 new.email = email
222 'email':email,
224 new.name = name
223 'name':name,
225 new.lastname = lastname
224 'lastname':lastname})
226 new.api_key = generate_api_key(username)
225 #register new user for email test
227 self.sa.add(new)
226 response = self.app.post(url(controller='login', action='password_reset'),
228 self.sa.commit()
227 {'email':email, })
229
228 print response.session['flash']
230 response = self.app.post(url(controller='login',
229 assert 'You have successfully registered into rhodecode' in response.session['flash'][0], 'No flash message about user registration'
231 action='password_reset'),
230 assert 'Your new password was sent' in response.session['flash'][1], 'No flash message about password reset'
232 {'email':email, })
233
234 self.checkSessionFlash(response, 'Your password reset link was sent')
235
236 response = response.follow()
237
238 # BAD KEY
239
240 key = "bad"
241 response = self.app.get(url(controller='login',
242 action='password_reset_confirmation',
243 key=key))
244 self.assertEqual(response.status, '302 Found')
245 self.assertTrue(response.location.endswith(url('reset_password')))
246
247 # GOOD KEY
248
249 key = User.by_username(username).api_key
250
251 response = self.app.get(url(controller='login',
252 action='password_reset_confirmation',
253 key=key))
254 self.assertEqual(response.status, '302 Found')
255 self.assertTrue(response.location.endswith(url('login_home')))
256
257 self.checkSessionFlash(response,
258 ('Your password reset was successful, '
259 'new password has been sent to your email'))
260
261 response = response.follow()
@@ -1,43 +1,43 b''
1 [egg_info]
1 [egg_info]
2 tag_build = beta
2 tag_build = beta
3 tag_svn_revision = true
3 tag_svn_revision = true
4
4
5 [easy_install]
5 [easy_install]
6 find_links = http://www.pylonshq.com/download/
6 find_links = http://www.pylonshq.com/download/
7
7
8 [nosetests]
8 [nosetests]
9 verbose=False
9 verbose=True
10 verbosity=2
10 verbosity=2
11 with-pylons=test.ini
11 with-pylons=test.ini
12 detailed-errors=0
12 detailed-errors=1
13 nologcapture=1
13 nologcapture=1
14
14
15 # Babel configuration
15 # Babel configuration
16 [compile_catalog]
16 [compile_catalog]
17 domain = rhodecode
17 domain = rhodecode
18 directory = rhodecode/i18n
18 directory = rhodecode/i18n
19 statistics = true
19 statistics = true
20
20
21 [extract_messages]
21 [extract_messages]
22 add_comments = TRANSLATORS:
22 add_comments = TRANSLATORS:
23 output_file = rhodecode/i18n/rhodecode.pot
23 output_file = rhodecode/i18n/rhodecode.pot
24 width = 80
24 width = 80
25
25
26 [init_catalog]
26 [init_catalog]
27 domain = rhodecode
27 domain = rhodecode
28 input_file = rhodecode/i18n/rhodecode.pot
28 input_file = rhodecode/i18n/rhodecode.pot
29 output_dir = rhodecode/i18n
29 output_dir = rhodecode/i18n
30
30
31 [update_catalog]
31 [update_catalog]
32 domain = rhodecode
32 domain = rhodecode
33 input_file = rhodecode/i18n/rhodecode.pot
33 input_file = rhodecode/i18n/rhodecode.pot
34 output_dir = rhodecode/i18n
34 output_dir = rhodecode/i18n
35 previous = true
35 previous = true
36
36
37 [build_sphinx]
37 [build_sphinx]
38 source-dir = docs/
38 source-dir = docs/
39 build-dir = docs/_build
39 build-dir = docs/_build
40 all_files = 1
40 all_files = 1
41
41
42 [upload_sphinx]
42 [upload_sphinx]
43 upload-dir = docs/_build/html No newline at end of file
43 upload-dir = docs/_build/html
General Comments 0
You need to be logged in to leave comments. Login now