##// END OF EJS Templates
merged + fixed pull request #62: Implemented metatags and visualisation options....
marcink -
r2674:a221706d beta
parent child Browse files
Show More
@@ -1,485 +1,486 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.repos
3 rhodecode.controllers.admin.repos
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Repositories controller for RhodeCode
6 Repositories controller for RhodeCode
7
7
8 :created_on: Apr 7, 2010
8 :created_on: Apr 7, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 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 import formencode
28 import formencode
29 from formencode import htmlfill
29 from formencode import htmlfill
30
30
31 from webob.exc import HTTPInternalServerError
31 from webob.exc import HTTPInternalServerError
32 from pylons import request, session, tmpl_context as c, url
32 from pylons import request, session, tmpl_context as c, url
33 from pylons.controllers.util import redirect
33 from pylons.controllers.util import redirect
34 from pylons.i18n.translation import _
34 from pylons.i18n.translation import _
35 from sqlalchemy.exc import IntegrityError
35 from sqlalchemy.exc import IntegrityError
36
36
37 import rhodecode
37 import rhodecode
38 from rhodecode.lib import helpers as h
38 from rhodecode.lib import helpers as h
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
40 HasPermissionAnyDecorator, HasRepoPermissionAllDecorator
40 HasPermissionAnyDecorator, HasRepoPermissionAllDecorator
41 from rhodecode.lib.base import BaseController, render
41 from rhodecode.lib.base import BaseController, render
42 from rhodecode.lib.utils import invalidate_cache, action_logger, repo_name_slug
42 from rhodecode.lib.utils import invalidate_cache, action_logger, repo_name_slug
43 from rhodecode.lib.helpers import get_token
43 from rhodecode.lib.helpers import get_token
44 from rhodecode.model.meta import Session
44 from rhodecode.model.meta import Session
45 from rhodecode.model.db import User, Repository, UserFollowing, RepoGroup
45 from rhodecode.model.db import User, Repository, UserFollowing, RepoGroup
46 from rhodecode.model.forms import RepoForm
46 from rhodecode.model.forms import RepoForm
47 from rhodecode.model.scm import ScmModel
47 from rhodecode.model.scm import ScmModel
48 from rhodecode.model.repo import RepoModel
48 from rhodecode.model.repo import RepoModel
49 from rhodecode.lib.compat import json
49 from rhodecode.lib.compat import json
50
50
51 log = logging.getLogger(__name__)
51 log = logging.getLogger(__name__)
52
52
53
53
54 class ReposController(BaseController):
54 class ReposController(BaseController):
55 """
55 """
56 REST Controller styled on the Atom Publishing Protocol"""
56 REST Controller styled on the Atom Publishing Protocol"""
57 # To properly map this controller, ensure your config/routing.py
57 # To properly map this controller, ensure your config/routing.py
58 # file has a resource setup:
58 # file has a resource setup:
59 # map.resource('repo', 'repos')
59 # map.resource('repo', 'repos')
60
60
61 @LoginRequired()
61 @LoginRequired()
62 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
62 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
63 def __before__(self):
63 def __before__(self):
64 c.admin_user = session.get('admin_user')
64 c.admin_user = session.get('admin_user')
65 c.admin_username = session.get('admin_username')
65 c.admin_username = session.get('admin_username')
66 super(ReposController, self).__before__()
66 super(ReposController, self).__before__()
67
67
68 def __load_defaults(self):
68 def __load_defaults(self):
69 c.repo_groups = RepoGroup.groups_choices()
69 c.repo_groups = RepoGroup.groups_choices()
70 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
70 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
71
71
72 repo_model = RepoModel()
72 repo_model = RepoModel()
73 c.users_array = repo_model.get_users_js()
73 c.users_array = repo_model.get_users_js()
74 c.users_groups_array = repo_model.get_users_groups_js()
74 c.users_groups_array = repo_model.get_users_groups_js()
75 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
75 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
76 c.landing_revs_choices = choices
76 c.landing_revs_choices = choices
77
77
78 def __load_data(self, repo_name=None):
78 def __load_data(self, repo_name=None):
79 """
79 """
80 Load defaults settings for edit, and update
80 Load defaults settings for edit, and update
81
81
82 :param repo_name:
82 :param repo_name:
83 """
83 """
84 self.__load_defaults()
84 self.__load_defaults()
85
85
86 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
86 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
87 repo = db_repo.scm_instance
87 repo = db_repo.scm_instance
88
88
89 if c.repo_info is None:
89 if c.repo_info is None:
90 h.flash(_('%s repository is not mapped to db perhaps'
90 h.flash(_('%s repository is not mapped to db perhaps'
91 ' it was created or renamed from the filesystem'
91 ' it was created or renamed from the filesystem'
92 ' please run the application again'
92 ' please run the application again'
93 ' in order to rescan repositories') % repo_name,
93 ' in order to rescan repositories') % repo_name,
94 category='error')
94 category='error')
95
95
96 return redirect(url('repos'))
96 return redirect(url('repos'))
97
97
98 choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info)
98 choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info)
99 c.landing_revs_choices = choices
99 c.landing_revs_choices = choices
100
100
101 c.default_user_id = User.get_by_username('default').user_id
101 c.default_user_id = User.get_by_username('default').user_id
102 c.in_public_journal = UserFollowing.query()\
102 c.in_public_journal = UserFollowing.query()\
103 .filter(UserFollowing.user_id == c.default_user_id)\
103 .filter(UserFollowing.user_id == c.default_user_id)\
104 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
104 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
105
105
106 if c.repo_info.stats:
106 if c.repo_info.stats:
107 # this is on what revision we ended up so we add +1 for count
107 # this is on what revision we ended up so we add +1 for count
108 last_rev = c.repo_info.stats.stat_on_revision + 1
108 last_rev = c.repo_info.stats.stat_on_revision + 1
109 else:
109 else:
110 last_rev = 0
110 last_rev = 0
111 c.stats_revision = last_rev
111 c.stats_revision = last_rev
112
112
113 c.repo_last_rev = repo.count() if repo.revisions else 0
113 c.repo_last_rev = repo.count() if repo.revisions else 0
114
114
115 if last_rev == 0 or c.repo_last_rev == 0:
115 if last_rev == 0 or c.repo_last_rev == 0:
116 c.stats_percentage = 0
116 c.stats_percentage = 0
117 else:
117 else:
118 c.stats_percentage = '%.2f' % ((float((last_rev)) /
118 c.stats_percentage = '%.2f' % ((float((last_rev)) /
119 c.repo_last_rev) * 100)
119 c.repo_last_rev) * 100)
120
120
121 defaults = RepoModel()._get_defaults(repo_name)
121 defaults = RepoModel()._get_defaults(repo_name)
122
122
123 c.repos_list = [('', _('--REMOVE FORK--'))]
123 c.repos_list = [('', _('--REMOVE FORK--'))]
124 c.repos_list += [(x.repo_id, x.repo_name) for x in
124 c.repos_list += [(x.repo_id, x.repo_name) for x in
125 Repository.query().order_by(Repository.repo_name).all()
125 Repository.query().order_by(Repository.repo_name).all()
126 if x.repo_id != c.repo_info.repo_id]
126 if x.repo_id != c.repo_info.repo_id]
127
127
128 defaults['id_fork_of'] = db_repo.fork.repo_id if db_repo.fork else ''
128 defaults['id_fork_of'] = db_repo.fork.repo_id if db_repo.fork else ''
129 return defaults
129 return defaults
130
130
131 @HasPermissionAllDecorator('hg.admin')
131 @HasPermissionAllDecorator('hg.admin')
132 def index(self, format='html'):
132 def index(self, format='html'):
133 """GET /repos: All items in the collection"""
133 """GET /repos: All items in the collection"""
134 # url('repos')
134 # url('repos')
135
135
136 c.repos_list = Repository.query()\
136 c.repos_list = Repository.query()\
137 .order_by(Repository.repo_name)\
137 .order_by(Repository.repo_name)\
138 .all()
138 .all()
139
139
140 repos_data = []
140 repos_data = []
141 total_records = len(c.repos_list)
141 total_records = len(c.repos_list)
142
142
143 _tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
143 _tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
144 template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
144 template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
145
145
146 quick_menu = lambda repo_name: (template.get_def("quick_menu")
146 quick_menu = lambda repo_name: (template.get_def("quick_menu")
147 .render(repo_name, _=_, h=h))
147 .render(repo_name, _=_, h=h, c=c))
148 repo_lnk = lambda name, rtype, private, fork_of: (
148 repo_lnk = lambda name, rtype, private, fork_of: (
149 template.get_def("repo_name")
149 template.get_def("repo_name")
150 .render(name, rtype, private, fork_of, short_name=False,
150 .render(name, rtype, private, fork_of, short_name=False,
151 admin=True, _=_, h=h))
151 admin=True, _=_, h=h, c=c))
152
152
153 repo_actions = lambda repo_name: (template.get_def("repo_actions")
153 repo_actions = lambda repo_name: (template.get_def("repo_actions")
154 .render(repo_name, _=_, h=h))
154 .render(repo_name, _=_, h=h, c=c))
155
155
156 for repo in c.repos_list:
156 for repo in c.repos_list:
157 repos_data.append({
157 repos_data.append({
158 "menu": quick_menu(repo.repo_name),
158 "menu": quick_menu(repo.repo_name),
159 "raw_name": repo.repo_name,
159 "raw_name": repo.repo_name,
160 "name": repo_lnk(repo.repo_name, repo.repo_type, repo.private, repo.fork),
160 "name": repo_lnk(repo.repo_name, repo.repo_type,
161 repo.private, repo.fork),
161 "desc": repo.description,
162 "desc": repo.description,
162 "owner": repo.user.username,
163 "owner": repo.user.username,
163 "action": repo_actions(repo.repo_name),
164 "action": repo_actions(repo.repo_name),
164 })
165 })
165
166
166 c.data = json.dumps({
167 c.data = json.dumps({
167 "totalRecords": total_records,
168 "totalRecords": total_records,
168 "startIndex": 0,
169 "startIndex": 0,
169 "sort": "name",
170 "sort": "name",
170 "dir": "asc",
171 "dir": "asc",
171 "records": repos_data
172 "records": repos_data
172 })
173 })
173
174
174 return render('admin/repos/repos.html')
175 return render('admin/repos/repos.html')
175
176
176 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
177 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
177 def create(self):
178 def create(self):
178 """
179 """
179 POST /repos: Create a new item"""
180 POST /repos: Create a new item"""
180 # url('repos')
181 # url('repos')
181
182
182 self.__load_defaults()
183 self.__load_defaults()
183 form_result = {}
184 form_result = {}
184 try:
185 try:
185 form_result = RepoForm(repo_groups=c.repo_groups_choices,
186 form_result = RepoForm(repo_groups=c.repo_groups_choices,
186 landing_revs=c.landing_revs_choices)()\
187 landing_revs=c.landing_revs_choices)()\
187 .to_python(dict(request.POST))
188 .to_python(dict(request.POST))
188 new_repo = RepoModel().create(form_result,
189 new_repo = RepoModel().create(form_result,
189 self.rhodecode_user.user_id)
190 self.rhodecode_user.user_id)
190 if form_result['clone_uri']:
191 if form_result['clone_uri']:
191 h.flash(_('created repository %s from %s') \
192 h.flash(_('created repository %s from %s') \
192 % (form_result['repo_name'], form_result['clone_uri']),
193 % (form_result['repo_name'], form_result['clone_uri']),
193 category='success')
194 category='success')
194 else:
195 else:
195 h.flash(_('created repository %s') % form_result['repo_name'],
196 h.flash(_('created repository %s') % form_result['repo_name'],
196 category='success')
197 category='success')
197
198
198 if request.POST.get('user_created'):
199 if request.POST.get('user_created'):
199 # created by regular non admin user
200 # created by regular non admin user
200 action_logger(self.rhodecode_user, 'user_created_repo',
201 action_logger(self.rhodecode_user, 'user_created_repo',
201 form_result['repo_name_full'], self.ip_addr,
202 form_result['repo_name_full'], self.ip_addr,
202 self.sa)
203 self.sa)
203 else:
204 else:
204 action_logger(self.rhodecode_user, 'admin_created_repo',
205 action_logger(self.rhodecode_user, 'admin_created_repo',
205 form_result['repo_name_full'], self.ip_addr,
206 form_result['repo_name_full'], self.ip_addr,
206 self.sa)
207 self.sa)
207 Session().commit()
208 Session().commit()
208 except formencode.Invalid, errors:
209 except formencode.Invalid, errors:
209
210
210 c.new_repo = errors.value['repo_name']
211 c.new_repo = errors.value['repo_name']
211
212
212 if request.POST.get('user_created'):
213 if request.POST.get('user_created'):
213 r = render('admin/repos/repo_add_create_repository.html')
214 r = render('admin/repos/repo_add_create_repository.html')
214 else:
215 else:
215 r = render('admin/repos/repo_add.html')
216 r = render('admin/repos/repo_add.html')
216
217
217 return htmlfill.render(
218 return htmlfill.render(
218 r,
219 r,
219 defaults=errors.value,
220 defaults=errors.value,
220 errors=errors.error_dict or {},
221 errors=errors.error_dict or {},
221 prefix_error=False,
222 prefix_error=False,
222 encoding="UTF-8")
223 encoding="UTF-8")
223
224
224 except Exception:
225 except Exception:
225 log.error(traceback.format_exc())
226 log.error(traceback.format_exc())
226 msg = _('error occurred during creation of repository %s') \
227 msg = _('error occurred during creation of repository %s') \
227 % form_result.get('repo_name')
228 % form_result.get('repo_name')
228 h.flash(msg, category='error')
229 h.flash(msg, category='error')
229 #redirect to our new repo !
230 #redirect to our new repo !
230 return redirect(url('summary_home', repo_name=new_repo.repo_name))
231 return redirect(url('summary_home', repo_name=new_repo.repo_name))
231
232
232 @HasPermissionAllDecorator('hg.admin')
233 @HasPermissionAllDecorator('hg.admin')
233 def new(self, format='html'):
234 def new(self, format='html'):
234 """GET /repos/new: Form to create a new item"""
235 """GET /repos/new: Form to create a new item"""
235 new_repo = request.GET.get('repo', '')
236 new_repo = request.GET.get('repo', '')
236 c.new_repo = repo_name_slug(new_repo)
237 c.new_repo = repo_name_slug(new_repo)
237 self.__load_defaults()
238 self.__load_defaults()
238 return render('admin/repos/repo_add.html')
239 return render('admin/repos/repo_add.html')
239
240
240 @HasPermissionAllDecorator('hg.admin')
241 @HasPermissionAllDecorator('hg.admin')
241 def update(self, repo_name):
242 def update(self, repo_name):
242 """
243 """
243 PUT /repos/repo_name: Update an existing item"""
244 PUT /repos/repo_name: Update an existing item"""
244 # Forms posted to this method should contain a hidden field:
245 # Forms posted to this method should contain a hidden field:
245 # <input type="hidden" name="_method" value="PUT" />
246 # <input type="hidden" name="_method" value="PUT" />
246 # Or using helpers:
247 # Or using helpers:
247 # h.form(url('repo', repo_name=ID),
248 # h.form(url('repo', repo_name=ID),
248 # method='put')
249 # method='put')
249 # url('repo', repo_name=ID)
250 # url('repo', repo_name=ID)
250 self.__load_defaults()
251 self.__load_defaults()
251 repo_model = RepoModel()
252 repo_model = RepoModel()
252 changed_name = repo_name
253 changed_name = repo_name
253 #override the choices with extracted revisions !
254 #override the choices with extracted revisions !
254 choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo_name)
255 choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo_name)
255 c.landing_revs_choices = choices
256 c.landing_revs_choices = choices
256
257
257 _form = RepoForm(edit=True, old_data={'repo_name': repo_name},
258 _form = RepoForm(edit=True, old_data={'repo_name': repo_name},
258 repo_groups=c.repo_groups_choices,
259 repo_groups=c.repo_groups_choices,
259 landing_revs=c.landing_revs_choices)()
260 landing_revs=c.landing_revs_choices)()
260 try:
261 try:
261 form_result = _form.to_python(dict(request.POST))
262 form_result = _form.to_python(dict(request.POST))
262 repo = repo_model.update(repo_name, form_result)
263 repo = repo_model.update(repo_name, form_result)
263 invalidate_cache('get_repo_cached_%s' % repo_name)
264 invalidate_cache('get_repo_cached_%s' % repo_name)
264 h.flash(_('Repository %s updated successfully') % repo_name,
265 h.flash(_('Repository %s updated successfully') % repo_name,
265 category='success')
266 category='success')
266 changed_name = repo.repo_name
267 changed_name = repo.repo_name
267 action_logger(self.rhodecode_user, 'admin_updated_repo',
268 action_logger(self.rhodecode_user, 'admin_updated_repo',
268 changed_name, self.ip_addr, self.sa)
269 changed_name, self.ip_addr, self.sa)
269 Session().commit()
270 Session().commit()
270 except formencode.Invalid, errors:
271 except formencode.Invalid, errors:
271 defaults = self.__load_data(repo_name)
272 defaults = self.__load_data(repo_name)
272 defaults.update(errors.value)
273 defaults.update(errors.value)
273 return htmlfill.render(
274 return htmlfill.render(
274 render('admin/repos/repo_edit.html'),
275 render('admin/repos/repo_edit.html'),
275 defaults=defaults,
276 defaults=defaults,
276 errors=errors.error_dict or {},
277 errors=errors.error_dict or {},
277 prefix_error=False,
278 prefix_error=False,
278 encoding="UTF-8")
279 encoding="UTF-8")
279
280
280 except Exception:
281 except Exception:
281 log.error(traceback.format_exc())
282 log.error(traceback.format_exc())
282 h.flash(_('error occurred during update of repository %s') \
283 h.flash(_('error occurred during update of repository %s') \
283 % repo_name, category='error')
284 % repo_name, category='error')
284 return redirect(url('edit_repo', repo_name=changed_name))
285 return redirect(url('edit_repo', repo_name=changed_name))
285
286
286 @HasPermissionAllDecorator('hg.admin')
287 @HasPermissionAllDecorator('hg.admin')
287 def delete(self, repo_name):
288 def delete(self, repo_name):
288 """
289 """
289 DELETE /repos/repo_name: Delete an existing item"""
290 DELETE /repos/repo_name: Delete an existing item"""
290 # Forms posted to this method should contain a hidden field:
291 # Forms posted to this method should contain a hidden field:
291 # <input type="hidden" name="_method" value="DELETE" />
292 # <input type="hidden" name="_method" value="DELETE" />
292 # Or using helpers:
293 # Or using helpers:
293 # h.form(url('repo', repo_name=ID),
294 # h.form(url('repo', repo_name=ID),
294 # method='delete')
295 # method='delete')
295 # url('repo', repo_name=ID)
296 # url('repo', repo_name=ID)
296
297
297 repo_model = RepoModel()
298 repo_model = RepoModel()
298 repo = repo_model.get_by_repo_name(repo_name)
299 repo = repo_model.get_by_repo_name(repo_name)
299 if not repo:
300 if not repo:
300 h.flash(_('%s repository is not mapped to db perhaps'
301 h.flash(_('%s repository is not mapped to db perhaps'
301 ' it was moved or renamed from the filesystem'
302 ' it was moved or renamed from the filesystem'
302 ' please run the application again'
303 ' please run the application again'
303 ' in order to rescan repositories') % repo_name,
304 ' in order to rescan repositories') % repo_name,
304 category='error')
305 category='error')
305
306
306 return redirect(url('repos'))
307 return redirect(url('repos'))
307 try:
308 try:
308 action_logger(self.rhodecode_user, 'admin_deleted_repo',
309 action_logger(self.rhodecode_user, 'admin_deleted_repo',
309 repo_name, self.ip_addr, self.sa)
310 repo_name, self.ip_addr, self.sa)
310 repo_model.delete(repo)
311 repo_model.delete(repo)
311 invalidate_cache('get_repo_cached_%s' % repo_name)
312 invalidate_cache('get_repo_cached_%s' % repo_name)
312 h.flash(_('deleted repository %s') % repo_name, category='success')
313 h.flash(_('deleted repository %s') % repo_name, category='success')
313 Session().commit()
314 Session().commit()
314 except IntegrityError, e:
315 except IntegrityError, e:
315 if e.message.find('repositories_fork_id_fkey') != -1:
316 if e.message.find('repositories_fork_id_fkey') != -1:
316 log.error(traceback.format_exc())
317 log.error(traceback.format_exc())
317 h.flash(_('Cannot delete %s it still contains attached '
318 h.flash(_('Cannot delete %s it still contains attached '
318 'forks') % repo_name,
319 'forks') % repo_name,
319 category='warning')
320 category='warning')
320 else:
321 else:
321 log.error(traceback.format_exc())
322 log.error(traceback.format_exc())
322 h.flash(_('An error occurred during '
323 h.flash(_('An error occurred during '
323 'deletion of %s') % repo_name,
324 'deletion of %s') % repo_name,
324 category='error')
325 category='error')
325
326
326 except Exception, e:
327 except Exception, e:
327 log.error(traceback.format_exc())
328 log.error(traceback.format_exc())
328 h.flash(_('An error occurred during deletion of %s') % repo_name,
329 h.flash(_('An error occurred during deletion of %s') % repo_name,
329 category='error')
330 category='error')
330
331
331 return redirect(url('repos'))
332 return redirect(url('repos'))
332
333
333 @HasRepoPermissionAllDecorator('repository.admin')
334 @HasRepoPermissionAllDecorator('repository.admin')
334 def delete_perm_user(self, repo_name):
335 def delete_perm_user(self, repo_name):
335 """
336 """
336 DELETE an existing repository permission user
337 DELETE an existing repository permission user
337
338
338 :param repo_name:
339 :param repo_name:
339 """
340 """
340 try:
341 try:
341 RepoModel().revoke_user_permission(repo=repo_name,
342 RepoModel().revoke_user_permission(repo=repo_name,
342 user=request.POST['user_id'])
343 user=request.POST['user_id'])
343 Session().commit()
344 Session().commit()
344 except Exception:
345 except Exception:
345 log.error(traceback.format_exc())
346 log.error(traceback.format_exc())
346 h.flash(_('An error occurred during deletion of repository user'),
347 h.flash(_('An error occurred during deletion of repository user'),
347 category='error')
348 category='error')
348 raise HTTPInternalServerError()
349 raise HTTPInternalServerError()
349
350
350 @HasRepoPermissionAllDecorator('repository.admin')
351 @HasRepoPermissionAllDecorator('repository.admin')
351 def delete_perm_users_group(self, repo_name):
352 def delete_perm_users_group(self, repo_name):
352 """
353 """
353 DELETE an existing repository permission users group
354 DELETE an existing repository permission users group
354
355
355 :param repo_name:
356 :param repo_name:
356 """
357 """
357
358
358 try:
359 try:
359 RepoModel().revoke_users_group_permission(
360 RepoModel().revoke_users_group_permission(
360 repo=repo_name, group_name=request.POST['users_group_id']
361 repo=repo_name, group_name=request.POST['users_group_id']
361 )
362 )
362 Session().commit()
363 Session().commit()
363 except Exception:
364 except Exception:
364 log.error(traceback.format_exc())
365 log.error(traceback.format_exc())
365 h.flash(_('An error occurred during deletion of repository'
366 h.flash(_('An error occurred during deletion of repository'
366 ' users groups'),
367 ' users groups'),
367 category='error')
368 category='error')
368 raise HTTPInternalServerError()
369 raise HTTPInternalServerError()
369
370
370 @HasPermissionAllDecorator('hg.admin')
371 @HasPermissionAllDecorator('hg.admin')
371 def repo_stats(self, repo_name):
372 def repo_stats(self, repo_name):
372 """
373 """
373 DELETE an existing repository statistics
374 DELETE an existing repository statistics
374
375
375 :param repo_name:
376 :param repo_name:
376 """
377 """
377
378
378 try:
379 try:
379 RepoModel().delete_stats(repo_name)
380 RepoModel().delete_stats(repo_name)
380 Session().commit()
381 Session().commit()
381 except Exception, e:
382 except Exception, e:
382 h.flash(_('An error occurred during deletion of repository stats'),
383 h.flash(_('An error occurred during deletion of repository stats'),
383 category='error')
384 category='error')
384 return redirect(url('edit_repo', repo_name=repo_name))
385 return redirect(url('edit_repo', repo_name=repo_name))
385
386
386 @HasPermissionAllDecorator('hg.admin')
387 @HasPermissionAllDecorator('hg.admin')
387 def repo_cache(self, repo_name):
388 def repo_cache(self, repo_name):
388 """
389 """
389 INVALIDATE existing repository cache
390 INVALIDATE existing repository cache
390
391
391 :param repo_name:
392 :param repo_name:
392 """
393 """
393
394
394 try:
395 try:
395 ScmModel().mark_for_invalidation(repo_name)
396 ScmModel().mark_for_invalidation(repo_name)
396 Session().commit()
397 Session().commit()
397 except Exception, e:
398 except Exception, e:
398 h.flash(_('An error occurred during cache invalidation'),
399 h.flash(_('An error occurred during cache invalidation'),
399 category='error')
400 category='error')
400 return redirect(url('edit_repo', repo_name=repo_name))
401 return redirect(url('edit_repo', repo_name=repo_name))
401
402
402 @HasPermissionAllDecorator('hg.admin')
403 @HasPermissionAllDecorator('hg.admin')
403 def repo_public_journal(self, repo_name):
404 def repo_public_journal(self, repo_name):
404 """
405 """
405 Set's this repository to be visible in public journal,
406 Set's this repository to be visible in public journal,
406 in other words assing default user to follow this repo
407 in other words assing default user to follow this repo
407
408
408 :param repo_name:
409 :param repo_name:
409 """
410 """
410
411
411 cur_token = request.POST.get('auth_token')
412 cur_token = request.POST.get('auth_token')
412 token = get_token()
413 token = get_token()
413 if cur_token == token:
414 if cur_token == token:
414 try:
415 try:
415 repo_id = Repository.get_by_repo_name(repo_name).repo_id
416 repo_id = Repository.get_by_repo_name(repo_name).repo_id
416 user_id = User.get_by_username('default').user_id
417 user_id = User.get_by_username('default').user_id
417 self.scm_model.toggle_following_repo(repo_id, user_id)
418 self.scm_model.toggle_following_repo(repo_id, user_id)
418 h.flash(_('Updated repository visibility in public journal'),
419 h.flash(_('Updated repository visibility in public journal'),
419 category='success')
420 category='success')
420 Session().commit()
421 Session().commit()
421 except:
422 except:
422 h.flash(_('An error occurred during setting this'
423 h.flash(_('An error occurred during setting this'
423 ' repository in public journal'),
424 ' repository in public journal'),
424 category='error')
425 category='error')
425
426
426 else:
427 else:
427 h.flash(_('Token mismatch'), category='error')
428 h.flash(_('Token mismatch'), category='error')
428 return redirect(url('edit_repo', repo_name=repo_name))
429 return redirect(url('edit_repo', repo_name=repo_name))
429
430
430 @HasPermissionAllDecorator('hg.admin')
431 @HasPermissionAllDecorator('hg.admin')
431 def repo_pull(self, repo_name):
432 def repo_pull(self, repo_name):
432 """
433 """
433 Runs task to update given repository with remote changes,
434 Runs task to update given repository with remote changes,
434 ie. make pull on remote location
435 ie. make pull on remote location
435
436
436 :param repo_name:
437 :param repo_name:
437 """
438 """
438 try:
439 try:
439 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
440 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
440 h.flash(_('Pulled from remote location'), category='success')
441 h.flash(_('Pulled from remote location'), category='success')
441 except Exception, e:
442 except Exception, e:
442 h.flash(_('An error occurred during pull from remote location'),
443 h.flash(_('An error occurred during pull from remote location'),
443 category='error')
444 category='error')
444
445
445 return redirect(url('edit_repo', repo_name=repo_name))
446 return redirect(url('edit_repo', repo_name=repo_name))
446
447
447 @HasPermissionAllDecorator('hg.admin')
448 @HasPermissionAllDecorator('hg.admin')
448 def repo_as_fork(self, repo_name):
449 def repo_as_fork(self, repo_name):
449 """
450 """
450 Mark given repository as a fork of another
451 Mark given repository as a fork of another
451
452
452 :param repo_name:
453 :param repo_name:
453 """
454 """
454 try:
455 try:
455 fork_id = request.POST.get('id_fork_of')
456 fork_id = request.POST.get('id_fork_of')
456 repo = ScmModel().mark_as_fork(repo_name, fork_id,
457 repo = ScmModel().mark_as_fork(repo_name, fork_id,
457 self.rhodecode_user.username)
458 self.rhodecode_user.username)
458 fork = repo.fork.repo_name if repo.fork else _('Nothing')
459 fork = repo.fork.repo_name if repo.fork else _('Nothing')
459 Session().commit()
460 Session().commit()
460 h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork),
461 h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork),
461 category='success')
462 category='success')
462 except Exception, e:
463 except Exception, e:
463 log.error(traceback.format_exc())
464 log.error(traceback.format_exc())
464 h.flash(_('An error occurred during this operation'),
465 h.flash(_('An error occurred during this operation'),
465 category='error')
466 category='error')
466
467
467 return redirect(url('edit_repo', repo_name=repo_name))
468 return redirect(url('edit_repo', repo_name=repo_name))
468
469
469 @HasPermissionAllDecorator('hg.admin')
470 @HasPermissionAllDecorator('hg.admin')
470 def show(self, repo_name, format='html'):
471 def show(self, repo_name, format='html'):
471 """GET /repos/repo_name: Show a specific item"""
472 """GET /repos/repo_name: Show a specific item"""
472 # url('repo', repo_name=ID)
473 # url('repo', repo_name=ID)
473
474
474 @HasPermissionAllDecorator('hg.admin')
475 @HasPermissionAllDecorator('hg.admin')
475 def edit(self, repo_name, format='html'):
476 def edit(self, repo_name, format='html'):
476 """GET /repos/repo_name/edit: Form to edit an existing item"""
477 """GET /repos/repo_name/edit: Form to edit an existing item"""
477 # url('edit_repo', repo_name=ID)
478 # url('edit_repo', repo_name=ID)
478 defaults = self.__load_data(repo_name)
479 defaults = self.__load_data(repo_name)
479
480
480 return htmlfill.render(
481 return htmlfill.render(
481 render('admin/repos/repo_edit.html'),
482 render('admin/repos/repo_edit.html'),
482 defaults=defaults,
483 defaults=defaults,
483 encoding="UTF-8",
484 encoding="UTF-8",
484 force_defaults=False
485 force_defaults=False
485 )
486 )
@@ -1,438 +1,479 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.settings
3 rhodecode.controllers.admin.settings
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 settings controller for rhodecode admin
6 settings controller for rhodecode admin
7
7
8 :created_on: Jul 14, 2010
8 :created_on: Jul 14, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 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 import formencode
28 import formencode
29 import pkg_resources
29 import pkg_resources
30 import platform
30 import platform
31
31
32 from sqlalchemy import func
32 from sqlalchemy import func
33 from formencode import htmlfill
33 from formencode import htmlfill
34 from pylons import request, session, tmpl_context as c, url, config
34 from pylons import request, session, tmpl_context as c, url, config
35 from pylons.controllers.util import abort, redirect
35 from pylons.controllers.util import abort, redirect
36 from pylons.i18n.translation import _
36 from pylons.i18n.translation import _
37
37
38 from rhodecode.lib import helpers as h
38 from rhodecode.lib import helpers as h
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
40 HasPermissionAnyDecorator, NotAnonymous
40 HasPermissionAnyDecorator, NotAnonymous
41 from rhodecode.lib.base import BaseController, render
41 from rhodecode.lib.base import BaseController, render
42 from rhodecode.lib.celerylib import tasks, run_task
42 from rhodecode.lib.celerylib import tasks, run_task
43 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
43 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
44 set_rhodecode_config, repo_name_slug
44 set_rhodecode_config, repo_name_slug
45 from rhodecode.model.db import RhodeCodeUi, Repository, RepoGroup, \
45 from rhodecode.model.db import RhodeCodeUi, Repository, RepoGroup, \
46 RhodeCodeSetting, PullRequest, PullRequestReviewers
46 RhodeCodeSetting, PullRequest, PullRequestReviewers
47 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
47 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
48 ApplicationUiSettingsForm
48 ApplicationUiSettingsForm, ApplicationVisualisationForm
49 from rhodecode.model.scm import ScmModel
49 from rhodecode.model.scm import ScmModel
50 from rhodecode.model.user import UserModel
50 from rhodecode.model.user import UserModel
51 from rhodecode.model.db import User
51 from rhodecode.model.db import User
52 from rhodecode.model.notification import EmailNotificationModel
52 from rhodecode.model.notification import EmailNotificationModel
53 from rhodecode.model.meta import Session
53 from rhodecode.model.meta import Session
54
54
55 log = logging.getLogger(__name__)
55 log = logging.getLogger(__name__)
56
56
57
57
58 class SettingsController(BaseController):
58 class SettingsController(BaseController):
59 """REST Controller styled on the Atom Publishing Protocol"""
59 """REST Controller styled on the Atom Publishing Protocol"""
60 # To properly map this controller, ensure your config/routing.py
60 # To properly map this controller, ensure your config/routing.py
61 # file has a resource setup:
61 # file has a resource setup:
62 # map.resource('setting', 'settings', controller='admin/settings',
62 # map.resource('setting', 'settings', controller='admin/settings',
63 # path_prefix='/admin', name_prefix='admin_')
63 # path_prefix='/admin', name_prefix='admin_')
64
64
65 @LoginRequired()
65 @LoginRequired()
66 def __before__(self):
66 def __before__(self):
67 c.admin_user = session.get('admin_user')
67 c.admin_user = session.get('admin_user')
68 c.admin_username = session.get('admin_username')
68 c.admin_username = session.get('admin_username')
69 c.modules = sorted([(p.project_name, p.version)
69 c.modules = sorted([(p.project_name, p.version)
70 for p in pkg_resources.working_set],
70 for p in pkg_resources.working_set],
71 key=lambda k: k[0].lower())
71 key=lambda k: k[0].lower())
72 c.py_version = platform.python_version()
72 c.py_version = platform.python_version()
73 c.platform = platform.platform()
73 c.platform = platform.platform()
74 super(SettingsController, self).__before__()
74 super(SettingsController, self).__before__()
75
75
76 @HasPermissionAllDecorator('hg.admin')
76 @HasPermissionAllDecorator('hg.admin')
77 def index(self, format='html'):
77 def index(self, format='html'):
78 """GET /admin/settings: All items in the collection"""
78 """GET /admin/settings: All items in the collection"""
79 # url('admin_settings')
79 # url('admin_settings')
80
80
81 defaults = RhodeCodeSetting.get_app_settings()
81 defaults = RhodeCodeSetting.get_app_settings()
82 defaults.update(self.get_hg_ui_settings())
82 defaults.update(self.get_hg_ui_settings())
83
83
84 return htmlfill.render(
84 return htmlfill.render(
85 render('admin/settings/settings.html'),
85 render('admin/settings/settings.html'),
86 defaults=defaults,
86 defaults=defaults,
87 encoding="UTF-8",
87 encoding="UTF-8",
88 force_defaults=False
88 force_defaults=False
89 )
89 )
90
90
91 @HasPermissionAllDecorator('hg.admin')
91 @HasPermissionAllDecorator('hg.admin')
92 def create(self):
92 def create(self):
93 """POST /admin/settings: Create a new item"""
93 """POST /admin/settings: Create a new item"""
94 # url('admin_settings')
94 # url('admin_settings')
95
95
96 @HasPermissionAllDecorator('hg.admin')
96 @HasPermissionAllDecorator('hg.admin')
97 def new(self, format='html'):
97 def new(self, format='html'):
98 """GET /admin/settings/new: Form to create a new item"""
98 """GET /admin/settings/new: Form to create a new item"""
99 # url('admin_new_setting')
99 # url('admin_new_setting')
100
100
101 @HasPermissionAllDecorator('hg.admin')
101 @HasPermissionAllDecorator('hg.admin')
102 def update(self, setting_id):
102 def update(self, setting_id):
103 """PUT /admin/settings/setting_id: Update an existing item"""
103 """PUT /admin/settings/setting_id: Update an existing item"""
104 # Forms posted to this method should contain a hidden field:
104 # Forms posted to this method should contain a hidden field:
105 # <input type="hidden" name="_method" value="PUT" />
105 # <input type="hidden" name="_method" value="PUT" />
106 # Or using helpers:
106 # Or using helpers:
107 # h.form(url('admin_setting', setting_id=ID),
107 # h.form(url('admin_setting', setting_id=ID),
108 # method='put')
108 # method='put')
109 # url('admin_setting', setting_id=ID)
109 # url('admin_setting', setting_id=ID)
110
110
111 if setting_id == 'mapping':
111 if setting_id == 'mapping':
112 rm_obsolete = request.POST.get('destroy', False)
112 rm_obsolete = request.POST.get('destroy', False)
113 log.debug('Rescanning directories with destroy=%s' % rm_obsolete)
113 log.debug('Rescanning directories with destroy=%s' % rm_obsolete)
114 initial = ScmModel().repo_scan()
114 initial = ScmModel().repo_scan()
115 log.debug('invalidating all repositories')
115 log.debug('invalidating all repositories')
116 for repo_name in initial.keys():
116 for repo_name in initial.keys():
117 invalidate_cache('get_repo_cached_%s' % repo_name)
117 invalidate_cache('get_repo_cached_%s' % repo_name)
118
118
119 added, removed = repo2db_mapper(initial, rm_obsolete)
119 added, removed = repo2db_mapper(initial, rm_obsolete)
120
120
121 h.flash(_('Repositories successfully'
121 h.flash(_('Repositories successfully'
122 ' rescanned added: %s,removed: %s') % (added, removed),
122 ' rescanned added: %s,removed: %s') % (added, removed),
123 category='success')
123 category='success')
124
124
125 if setting_id == 'whoosh':
125 if setting_id == 'whoosh':
126 repo_location = self.get_hg_ui_settings()['paths_root_path']
126 repo_location = self.get_hg_ui_settings()['paths_root_path']
127 full_index = request.POST.get('full_index', False)
127 full_index = request.POST.get('full_index', False)
128 run_task(tasks.whoosh_index, repo_location, full_index)
128 run_task(tasks.whoosh_index, repo_location, full_index)
129 h.flash(_('Whoosh reindex task scheduled'), category='success')
129 h.flash(_('Whoosh reindex task scheduled'), category='success')
130
130
131 if setting_id == 'global':
131 if setting_id == 'global':
132
132
133 application_form = ApplicationSettingsForm()()
133 application_form = ApplicationSettingsForm()()
134 try:
134 try:
135 form_result = application_form.to_python(dict(request.POST))
135 form_result = application_form.to_python(dict(request.POST))
136 except formencode.Invalid, errors:
136 except formencode.Invalid, errors:
137 return htmlfill.render(
137 return htmlfill.render(
138 render('admin/settings/settings.html'),
138 render('admin/settings/settings.html'),
139 defaults=errors.value,
139 defaults=errors.value,
140 errors=errors.error_dict or {},
140 errors=errors.error_dict or {},
141 prefix_error=False,
141 prefix_error=False,
142 encoding="UTF-8"
142 encoding="UTF-8"
143 )
143 )
144
144
145 try:
145 try:
146 sett1 = RhodeCodeSetting.get_by_name('title')
146 sett1 = RhodeCodeSetting.get_by_name_or_create('title')
147 sett1.app_settings_value = form_result['rhodecode_title']
147 sett1.app_settings_value = form_result['rhodecode_title']
148 Session().add(sett1)
148 Session().add(sett1)
149
149
150 sett2 = RhodeCodeSetting.get_by_name('realm')
150 sett2 = RhodeCodeSetting.get_by_name_or_create('realm')
151 sett2.app_settings_value = form_result['rhodecode_realm']
151 sett2.app_settings_value = form_result['rhodecode_realm']
152 Session().add(sett2)
152 Session().add(sett2)
153
153
154 sett3 = RhodeCodeSetting.get_by_name('ga_code')
154 sett3 = RhodeCodeSetting.get_by_name_or_create('ga_code')
155 sett3.app_settings_value = form_result['rhodecode_ga_code']
155 sett3.app_settings_value = form_result['rhodecode_ga_code']
156 Session().add(sett3)
156 Session().add(sett3)
157
157
158 Session().commit()
158 Session().commit()
159 set_rhodecode_config(config)
159 set_rhodecode_config(config)
160 h.flash(_('Updated application settings'), category='success')
160 h.flash(_('Updated application settings'), category='success')
161
161
162 except Exception:
162 except Exception:
163 log.error(traceback.format_exc())
163 log.error(traceback.format_exc())
164 h.flash(_('error occurred during updating '
164 h.flash(_('error occurred during updating '
165 'application settings'),
165 'application settings'),
166 category='error')
166 category='error')
167
167
168 if setting_id == 'visual':
169
170 application_form = ApplicationVisualisationForm()()
171 try:
172 form_result = application_form.to_python(dict(request.POST))
173 except formencode.Invalid, errors:
174 return htmlfill.render(
175 render('admin/settings/settings.html'),
176 defaults=errors.value,
177 errors=errors.error_dict or {},
178 prefix_error=False,
179 encoding="UTF-8"
180 )
181
182 try:
183 sett1 = RhodeCodeSetting.get_by_name_or_create('show_public_icon')
184 sett1.app_settings_value = \
185 form_result['rhodecode_show_public_icon']
186
187 sett2 = RhodeCodeSetting.get_by_name_or_create('show_private_icon')
188 sett2.app_settings_value = \
189 form_result['rhodecode_show_private_icon']
190
191 sett3 = RhodeCodeSetting.get_by_name_or_create('stylify_metatags')
192 sett3.app_settings_value = \
193 form_result['rhodecode_stylify_metatags']
194
195 Session().add(sett1)
196 Session().add(sett2)
197 Session().add(sett3)
198 Session().commit()
199 set_rhodecode_config(config)
200 h.flash(_('Updated visualisation settings'),
201 category='success')
202
203 except Exception:
204 log.error(traceback.format_exc())
205 h.flash(_('error occurred during updating '
206 'visualisation settings'),
207 category='error')
208
168 if setting_id == 'vcs':
209 if setting_id == 'vcs':
169 application_form = ApplicationUiSettingsForm()()
210 application_form = ApplicationUiSettingsForm()()
170 try:
211 try:
171 form_result = application_form.to_python(dict(request.POST))
212 form_result = application_form.to_python(dict(request.POST))
172 except formencode.Invalid, errors:
213 except formencode.Invalid, errors:
173 return htmlfill.render(
214 return htmlfill.render(
174 render('admin/settings/settings.html'),
215 render('admin/settings/settings.html'),
175 defaults=errors.value,
216 defaults=errors.value,
176 errors=errors.error_dict or {},
217 errors=errors.error_dict or {},
177 prefix_error=False,
218 prefix_error=False,
178 encoding="UTF-8"
219 encoding="UTF-8"
179 )
220 )
180
221
181 try:
222 try:
182 # fix namespaces for hooks
223 # fix namespaces for hooks
183 _f = lambda s: s.replace('.', '_')
224 _f = lambda s: s.replace('.', '_')
184
225
185 sett1 = RhodeCodeUi.query()\
226 sett1 = RhodeCodeUi.query()\
186 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
227 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
187 sett1.ui_value = form_result['web_push_ssl']
228 sett1.ui_value = form_result['web_push_ssl']
188
229
189 sett2 = RhodeCodeUi.query()\
230 sett2 = RhodeCodeUi.query()\
190 .filter(RhodeCodeUi.ui_key == '/').one()
231 .filter(RhodeCodeUi.ui_key == '/').one()
191 sett2.ui_value = form_result['paths_root_path']
232 sett2.ui_value = form_result['paths_root_path']
192
233
193 #HOOKS
234 #HOOKS
194 sett3 = RhodeCodeUi.query()\
235 sett3 = RhodeCodeUi.query()\
195 .filter(RhodeCodeUi.ui_key == RhodeCodeUi.HOOK_UPDATE)\
236 .filter(RhodeCodeUi.ui_key == RhodeCodeUi.HOOK_UPDATE)\
196 .one()
237 .one()
197 sett3.ui_active = bool(form_result[_f('hooks_%s' %
238 sett3.ui_active = bool(form_result[_f('hooks_%s' %
198 RhodeCodeUi.HOOK_UPDATE)])
239 RhodeCodeUi.HOOK_UPDATE)])
199
240
200 sett4 = RhodeCodeUi.query()\
241 sett4 = RhodeCodeUi.query()\
201 .filter(RhodeCodeUi.ui_key == RhodeCodeUi.HOOK_REPO_SIZE)\
242 .filter(RhodeCodeUi.ui_key == RhodeCodeUi.HOOK_REPO_SIZE)\
202 .one()
243 .one()
203 sett4.ui_active = bool(form_result[_f('hooks_%s' %
244 sett4.ui_active = bool(form_result[_f('hooks_%s' %
204 RhodeCodeUi.HOOK_REPO_SIZE)])
245 RhodeCodeUi.HOOK_REPO_SIZE)])
205
246
206 sett5 = RhodeCodeUi.query()\
247 sett5 = RhodeCodeUi.query()\
207 .filter(RhodeCodeUi.ui_key == RhodeCodeUi.HOOK_PUSH)\
248 .filter(RhodeCodeUi.ui_key == RhodeCodeUi.HOOK_PUSH)\
208 .one()
249 .one()
209 sett5.ui_active = bool(form_result[_f('hooks_%s' %
250 sett5.ui_active = bool(form_result[_f('hooks_%s' %
210 RhodeCodeUi.HOOK_PUSH)])
251 RhodeCodeUi.HOOK_PUSH)])
211
252
212 sett6 = RhodeCodeUi.query()\
253 sett6 = RhodeCodeUi.query()\
213 .filter(RhodeCodeUi.ui_key == RhodeCodeUi.HOOK_PULL)\
254 .filter(RhodeCodeUi.ui_key == RhodeCodeUi.HOOK_PULL)\
214 .one()
255 .one()
215 sett6.ui_active = bool(form_result[_f('hooks_%s' %
256 sett6.ui_active = bool(form_result[_f('hooks_%s' %
216 RhodeCodeUi.HOOK_PULL)])
257 RhodeCodeUi.HOOK_PULL)])
217
258
218 Session().add(sett1)
259 Session().add(sett1)
219 Session().add(sett2)
260 Session().add(sett2)
220 Session().add(sett3)
261 Session().add(sett3)
221 Session().add(sett4)
262 Session().add(sett4)
222 Session().add(sett5)
263 Session().add(sett5)
223 Session().add(sett6)
264 Session().add(sett6)
224 Session().commit()
265 Session().commit()
225
266
226 h.flash(_('Updated mercurial settings'), category='success')
267 h.flash(_('Updated mercurial settings'), category='success')
227
268
228 except Exception:
269 except Exception:
229 log.error(traceback.format_exc())
270 log.error(traceback.format_exc())
230 h.flash(_('error occurred during updating '
271 h.flash(_('error occurred during updating '
231 'application settings'), category='error')
272 'application settings'), category='error')
232
273
233 if setting_id == 'hooks':
274 if setting_id == 'hooks':
234 ui_key = request.POST.get('new_hook_ui_key')
275 ui_key = request.POST.get('new_hook_ui_key')
235 ui_value = request.POST.get('new_hook_ui_value')
276 ui_value = request.POST.get('new_hook_ui_value')
236 try:
277 try:
237
278
238 if ui_value and ui_key:
279 if ui_value and ui_key:
239 RhodeCodeUi.create_or_update_hook(ui_key, ui_value)
280 RhodeCodeUi.create_or_update_hook(ui_key, ui_value)
240 h.flash(_('Added new hook'),
281 h.flash(_('Added new hook'),
241 category='success')
282 category='success')
242
283
243 # check for edits
284 # check for edits
244 update = False
285 update = False
245 _d = request.POST.dict_of_lists()
286 _d = request.POST.dict_of_lists()
246 for k, v in zip(_d.get('hook_ui_key', []),
287 for k, v in zip(_d.get('hook_ui_key', []),
247 _d.get('hook_ui_value_new', [])):
288 _d.get('hook_ui_value_new', [])):
248 RhodeCodeUi.create_or_update_hook(k, v)
289 RhodeCodeUi.create_or_update_hook(k, v)
249 update = True
290 update = True
250
291
251 if update:
292 if update:
252 h.flash(_('Updated hooks'), category='success')
293 h.flash(_('Updated hooks'), category='success')
253 Session().commit()
294 Session().commit()
254 except Exception:
295 except Exception:
255 log.error(traceback.format_exc())
296 log.error(traceback.format_exc())
256 h.flash(_('error occurred during hook creation'),
297 h.flash(_('error occurred during hook creation'),
257 category='error')
298 category='error')
258
299
259 return redirect(url('admin_edit_setting', setting_id='hooks'))
300 return redirect(url('admin_edit_setting', setting_id='hooks'))
260
301
261 if setting_id == 'email':
302 if setting_id == 'email':
262 test_email = request.POST.get('test_email')
303 test_email = request.POST.get('test_email')
263 test_email_subj = 'RhodeCode TestEmail'
304 test_email_subj = 'RhodeCode TestEmail'
264 test_email_body = 'RhodeCode Email test'
305 test_email_body = 'RhodeCode Email test'
265
306
266 test_email_html_body = EmailNotificationModel()\
307 test_email_html_body = EmailNotificationModel()\
267 .get_email_tmpl(EmailNotificationModel.TYPE_DEFAULT,
308 .get_email_tmpl(EmailNotificationModel.TYPE_DEFAULT,
268 body=test_email_body)
309 body=test_email_body)
269
310
270 recipients = [test_email] if [test_email] else None
311 recipients = [test_email] if [test_email] else None
271
312
272 run_task(tasks.send_email, recipients, test_email_subj,
313 run_task(tasks.send_email, recipients, test_email_subj,
273 test_email_body, test_email_html_body)
314 test_email_body, test_email_html_body)
274
315
275 h.flash(_('Email task created'), category='success')
316 h.flash(_('Email task created'), category='success')
276 return redirect(url('admin_settings'))
317 return redirect(url('admin_settings'))
277
318
278 @HasPermissionAllDecorator('hg.admin')
319 @HasPermissionAllDecorator('hg.admin')
279 def delete(self, setting_id):
320 def delete(self, setting_id):
280 """DELETE /admin/settings/setting_id: Delete an existing item"""
321 """DELETE /admin/settings/setting_id: Delete an existing item"""
281 # Forms posted to this method should contain a hidden field:
322 # Forms posted to this method should contain a hidden field:
282 # <input type="hidden" name="_method" value="DELETE" />
323 # <input type="hidden" name="_method" value="DELETE" />
283 # Or using helpers:
324 # Or using helpers:
284 # h.form(url('admin_setting', setting_id=ID),
325 # h.form(url('admin_setting', setting_id=ID),
285 # method='delete')
326 # method='delete')
286 # url('admin_setting', setting_id=ID)
327 # url('admin_setting', setting_id=ID)
287 if setting_id == 'hooks':
328 if setting_id == 'hooks':
288 hook_id = request.POST.get('hook_id')
329 hook_id = request.POST.get('hook_id')
289 RhodeCodeUi.delete(hook_id)
330 RhodeCodeUi.delete(hook_id)
290 Session().commit()
331 Session().commit()
291
332
292 @HasPermissionAllDecorator('hg.admin')
333 @HasPermissionAllDecorator('hg.admin')
293 def show(self, setting_id, format='html'):
334 def show(self, setting_id, format='html'):
294 """
335 """
295 GET /admin/settings/setting_id: Show a specific item"""
336 GET /admin/settings/setting_id: Show a specific item"""
296 # url('admin_setting', setting_id=ID)
337 # url('admin_setting', setting_id=ID)
297
338
298 @HasPermissionAllDecorator('hg.admin')
339 @HasPermissionAllDecorator('hg.admin')
299 def edit(self, setting_id, format='html'):
340 def edit(self, setting_id, format='html'):
300 """
341 """
301 GET /admin/settings/setting_id/edit: Form to
342 GET /admin/settings/setting_id/edit: Form to
302 edit an existing item"""
343 edit an existing item"""
303 # url('admin_edit_setting', setting_id=ID)
344 # url('admin_edit_setting', setting_id=ID)
304
345
305 c.hooks = RhodeCodeUi.get_builtin_hooks()
346 c.hooks = RhodeCodeUi.get_builtin_hooks()
306 c.custom_hooks = RhodeCodeUi.get_custom_hooks()
347 c.custom_hooks = RhodeCodeUi.get_custom_hooks()
307
348
308 return htmlfill.render(
349 return htmlfill.render(
309 render('admin/settings/hooks.html'),
350 render('admin/settings/hooks.html'),
310 defaults={},
351 defaults={},
311 encoding="UTF-8",
352 encoding="UTF-8",
312 force_defaults=False
353 force_defaults=False
313 )
354 )
314
355
315 @NotAnonymous()
356 @NotAnonymous()
316 def my_account(self):
357 def my_account(self):
317 """
358 """
318 GET /_admin/my_account Displays info about my account
359 GET /_admin/my_account Displays info about my account
319 """
360 """
320 # url('admin_settings_my_account')
361 # url('admin_settings_my_account')
321
362
322 c.user = User.get(self.rhodecode_user.user_id)
363 c.user = User.get(self.rhodecode_user.user_id)
323 all_repos = Session().query(Repository)\
364 all_repos = Session().query(Repository)\
324 .filter(Repository.user_id == c.user.user_id)\
365 .filter(Repository.user_id == c.user.user_id)\
325 .order_by(func.lower(Repository.repo_name)).all()
366 .order_by(func.lower(Repository.repo_name)).all()
326
367
327 c.user_repos = ScmModel().get_repos(all_repos)
368 c.user_repos = ScmModel().get_repos(all_repos)
328
369
329 if c.user.username == 'default':
370 if c.user.username == 'default':
330 h.flash(_("You can't edit this user since it's"
371 h.flash(_("You can't edit this user since it's"
331 " crucial for entire application"), category='warning')
372 " crucial for entire application"), category='warning')
332 return redirect(url('users'))
373 return redirect(url('users'))
333
374
334 defaults = c.user.get_dict()
375 defaults = c.user.get_dict()
335
376
336 c.form = htmlfill.render(
377 c.form = htmlfill.render(
337 render('admin/users/user_edit_my_account_form.html'),
378 render('admin/users/user_edit_my_account_form.html'),
338 defaults=defaults,
379 defaults=defaults,
339 encoding="UTF-8",
380 encoding="UTF-8",
340 force_defaults=False
381 force_defaults=False
341 )
382 )
342 return render('admin/users/user_edit_my_account.html')
383 return render('admin/users/user_edit_my_account.html')
343
384
344 @NotAnonymous()
385 @NotAnonymous()
345 def my_account_update(self):
386 def my_account_update(self):
346 """PUT /_admin/my_account_update: Update an existing item"""
387 """PUT /_admin/my_account_update: Update an existing item"""
347 # Forms posted to this method should contain a hidden field:
388 # Forms posted to this method should contain a hidden field:
348 # <input type="hidden" name="_method" value="PUT" />
389 # <input type="hidden" name="_method" value="PUT" />
349 # Or using helpers:
390 # Or using helpers:
350 # h.form(url('admin_settings_my_account_update'),
391 # h.form(url('admin_settings_my_account_update'),
351 # method='put')
392 # method='put')
352 # url('admin_settings_my_account_update', id=ID)
393 # url('admin_settings_my_account_update', id=ID)
353 uid = self.rhodecode_user.user_id
394 uid = self.rhodecode_user.user_id
354 email = self.rhodecode_user.email
395 email = self.rhodecode_user.email
355 _form = UserForm(edit=True,
396 _form = UserForm(edit=True,
356 old_data={'user_id': uid, 'email': email})()
397 old_data={'user_id': uid, 'email': email})()
357 form_result = {}
398 form_result = {}
358 try:
399 try:
359 form_result = _form.to_python(dict(request.POST))
400 form_result = _form.to_python(dict(request.POST))
360 UserModel().update_my_account(uid, form_result)
401 UserModel().update_my_account(uid, form_result)
361 h.flash(_('Your account was updated successfully'),
402 h.flash(_('Your account was updated successfully'),
362 category='success')
403 category='success')
363 Session().commit()
404 Session().commit()
364 except formencode.Invalid, errors:
405 except formencode.Invalid, errors:
365 c.user = User.get(self.rhodecode_user.user_id)
406 c.user = User.get(self.rhodecode_user.user_id)
366
407
367 c.form = htmlfill.render(
408 c.form = htmlfill.render(
368 render('admin/users/user_edit_my_account_form.html'),
409 render('admin/users/user_edit_my_account_form.html'),
369 defaults=errors.value,
410 defaults=errors.value,
370 errors=errors.error_dict or {},
411 errors=errors.error_dict or {},
371 prefix_error=False,
412 prefix_error=False,
372 encoding="UTF-8")
413 encoding="UTF-8")
373 return render('admin/users/user_edit_my_account.html')
414 return render('admin/users/user_edit_my_account.html')
374 except Exception:
415 except Exception:
375 log.error(traceback.format_exc())
416 log.error(traceback.format_exc())
376 h.flash(_('error occurred during update of user %s') \
417 h.flash(_('error occurred during update of user %s') \
377 % form_result.get('username'), category='error')
418 % form_result.get('username'), category='error')
378
419
379 return redirect(url('my_account'))
420 return redirect(url('my_account'))
380
421
381 @NotAnonymous()
422 @NotAnonymous()
382 def my_account_my_repos(self):
423 def my_account_my_repos(self):
383 all_repos = Session().query(Repository)\
424 all_repos = Session().query(Repository)\
384 .filter(Repository.user_id == self.rhodecode_user.user_id)\
425 .filter(Repository.user_id == self.rhodecode_user.user_id)\
385 .order_by(func.lower(Repository.repo_name))\
426 .order_by(func.lower(Repository.repo_name))\
386 .all()
427 .all()
387 c.user_repos = ScmModel().get_repos(all_repos)
428 c.user_repos = ScmModel().get_repos(all_repos)
388 return render('admin/users/user_edit_my_account_repos.html')
429 return render('admin/users/user_edit_my_account_repos.html')
389
430
390 @NotAnonymous()
431 @NotAnonymous()
391 def my_account_my_pullrequests(self):
432 def my_account_my_pullrequests(self):
392 c.my_pull_requests = PullRequest.query()\
433 c.my_pull_requests = PullRequest.query()\
393 .filter(PullRequest.user_id==
434 .filter(PullRequest.user_id==
394 self.rhodecode_user.user_id)\
435 self.rhodecode_user.user_id)\
395 .all()
436 .all()
396 c.participate_in_pull_requests = \
437 c.participate_in_pull_requests = \
397 [x.pull_request for x in PullRequestReviewers.query()\
438 [x.pull_request for x in PullRequestReviewers.query()\
398 .filter(PullRequestReviewers.user_id==
439 .filter(PullRequestReviewers.user_id==
399 self.rhodecode_user.user_id)\
440 self.rhodecode_user.user_id)\
400 .all()]
441 .all()]
401 return render('admin/users/user_edit_my_account_pullrequests.html')
442 return render('admin/users/user_edit_my_account_pullrequests.html')
402
443
403 @NotAnonymous()
444 @NotAnonymous()
404 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
445 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
405 def create_repository(self):
446 def create_repository(self):
406 """GET /_admin/create_repository: Form to create a new item"""
447 """GET /_admin/create_repository: Form to create a new item"""
407
448
408 c.repo_groups = RepoGroup.groups_choices()
449 c.repo_groups = RepoGroup.groups_choices()
409 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
450 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
410 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
451 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
411
452
412 new_repo = request.GET.get('repo', '')
453 new_repo = request.GET.get('repo', '')
413 c.new_repo = repo_name_slug(new_repo)
454 c.new_repo = repo_name_slug(new_repo)
414
455
415 return render('admin/repos/repo_add_create_repository.html')
456 return render('admin/repos/repo_add_create_repository.html')
416
457
417 @NotAnonymous()
458 @NotAnonymous()
418 def get_hg_ui_settings(self):
459 def get_hg_ui_settings(self):
419 ret = RhodeCodeUi.query().all()
460 ret = RhodeCodeUi.query().all()
420
461
421 if not ret:
462 if not ret:
422 raise Exception('Could not get application ui settings !')
463 raise Exception('Could not get application ui settings !')
423 settings = {}
464 settings = {}
424 for each in ret:
465 for each in ret:
425 k = each.ui_key
466 k = each.ui_key
426 v = each.ui_value
467 v = each.ui_value
427 if k == '/':
468 if k == '/':
428 k = 'root_path'
469 k = 'root_path'
429
470
430 if k.find('.') != -1:
471 if k.find('.') != -1:
431 k = k.replace('.', '_')
472 k = k.replace('.', '_')
432
473
433 if each.ui_section == 'hooks':
474 if each.ui_section == 'hooks':
434 v = each.ui_active
475 v = each.ui_active
435
476
436 settings[each.ui_section + '_' + k] = v
477 settings[each.ui_section + '_' + k] = v
437
478
438 return settings
479 return settings
@@ -1,295 +1,295 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.users
3 rhodecode.controllers.admin.users
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Users crud controller for pylons
6 Users crud controller for pylons
7
7
8 :created_on: Apr 4, 2010
8 :created_on: Apr 4, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 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 import formencode
28 import formencode
29 from pylons import response
29 from pylons import response
30
30
31 from formencode import htmlfill
31 from formencode import htmlfill
32 from pylons import request, session, tmpl_context as c, url, config
32 from pylons import request, session, tmpl_context as c, url, config
33 from pylons.controllers.util import redirect
33 from pylons.controllers.util import redirect
34 from pylons.i18n.translation import _
34 from pylons.i18n.translation import _
35
35
36 from rhodecode.lib.exceptions import DefaultUserException, \
36 from rhodecode.lib.exceptions import DefaultUserException, \
37 UserOwnsReposException
37 UserOwnsReposException
38 from rhodecode.lib import helpers as h
38 from rhodecode.lib import helpers as h
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
40 AuthUser
40 AuthUser
41 from rhodecode.lib.base import BaseController, render
41 from rhodecode.lib.base import BaseController, render
42
42
43 import rhodecode
43 import rhodecode
44 from rhodecode.model.db import User, Permission, UserEmailMap
44 from rhodecode.model.db import User, Permission, UserEmailMap
45 from rhodecode.model.forms import UserForm
45 from rhodecode.model.forms import UserForm
46 from rhodecode.model.user import UserModel
46 from rhodecode.model.user import UserModel
47 from rhodecode.model.meta import Session
47 from rhodecode.model.meta import Session
48 from rhodecode.lib.utils import action_logger
48 from rhodecode.lib.utils import action_logger
49 from rhodecode.lib.compat import json
49 from rhodecode.lib.compat import json
50
50
51 log = logging.getLogger(__name__)
51 log = logging.getLogger(__name__)
52
52
53
53
54 class UsersController(BaseController):
54 class UsersController(BaseController):
55 """REST Controller styled on the Atom Publishing Protocol"""
55 """REST Controller styled on the Atom Publishing Protocol"""
56 # To properly map this controller, ensure your config/routing.py
56 # To properly map this controller, ensure your config/routing.py
57 # file has a resource setup:
57 # file has a resource setup:
58 # map.resource('user', 'users')
58 # map.resource('user', 'users')
59
59
60 @LoginRequired()
60 @LoginRequired()
61 @HasPermissionAllDecorator('hg.admin')
61 @HasPermissionAllDecorator('hg.admin')
62 def __before__(self):
62 def __before__(self):
63 c.admin_user = session.get('admin_user')
63 c.admin_user = session.get('admin_user')
64 c.admin_username = session.get('admin_username')
64 c.admin_username = session.get('admin_username')
65 super(UsersController, self).__before__()
65 super(UsersController, self).__before__()
66 c.available_permissions = config['available_permissions']
66 c.available_permissions = config['available_permissions']
67
67
68 def index(self, format='html'):
68 def index(self, format='html'):
69 """GET /users: All items in the collection"""
69 """GET /users: All items in the collection"""
70 # url('users')
70 # url('users')
71
71
72 c.users_list = User.query().order_by(User.username).all()
72 c.users_list = User.query().order_by(User.username).all()
73
73
74 users_data = []
74 users_data = []
75 total_records = len(c.users_list)
75 total_records = len(c.users_list)
76 _tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
76 _tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
77 template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
77 template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
78
78
79 grav_tmpl = lambda user_email, size: (
79 grav_tmpl = lambda user_email, size: (
80 template.get_def("user_gravatar")
80 template.get_def("user_gravatar")
81 .render(user_email, size, _=_, h=h))
81 .render(user_email, size, _=_, h=h, c=c))
82
82
83 user_lnk = lambda user_id, username: (
83 user_lnk = lambda user_id, username: (
84 template.get_def("user_name")
84 template.get_def("user_name")
85 .render(user_id, username, _=_, h=h))
85 .render(user_id, username, _=_, h=h, c=c))
86
86
87 user_actions = lambda user_id, username: (
87 user_actions = lambda user_id, username: (
88 template.get_def("user_actions")
88 template.get_def("user_actions")
89 .render(user_id, username, _=_, h=h))
89 .render(user_id, username, _=_, h=h, c=c))
90
90
91 for user in c.users_list:
91 for user in c.users_list:
92 users_data.append({
92 users_data.append({
93 "gravatar": grav_tmpl(user. email, 24),
93 "gravatar": grav_tmpl(user. email, 24),
94 "raw_username": user.username,
94 "raw_username": user.username,
95 "username": user_lnk(user.user_id, user.username),
95 "username": user_lnk(user.user_id, user.username),
96 "firstname": user.name,
96 "firstname": user.name,
97 "lastname": user.lastname,
97 "lastname": user.lastname,
98 "last_login": h.fmt_date(user.last_login),
98 "last_login": h.fmt_date(user.last_login),
99 "active": h.bool2icon(user.active),
99 "active": h.bool2icon(user.active),
100 "admin": h.bool2icon(user.admin),
100 "admin": h.bool2icon(user.admin),
101 "ldap": h.bool2icon(bool(user.ldap_dn)),
101 "ldap": h.bool2icon(bool(user.ldap_dn)),
102 "action": user_actions(user.user_id, user.username),
102 "action": user_actions(user.user_id, user.username),
103 })
103 })
104
104
105 c.data = json.dumps({
105 c.data = json.dumps({
106 "totalRecords": total_records,
106 "totalRecords": total_records,
107 "startIndex": 0,
107 "startIndex": 0,
108 "sort": None,
108 "sort": None,
109 "dir": "asc",
109 "dir": "asc",
110 "records": users_data
110 "records": users_data
111 })
111 })
112
112
113 return render('admin/users/users.html')
113 return render('admin/users/users.html')
114
114
115 def create(self):
115 def create(self):
116 """POST /users: Create a new item"""
116 """POST /users: Create a new item"""
117 # url('users')
117 # url('users')
118
118
119 user_model = UserModel()
119 user_model = UserModel()
120 user_form = UserForm()()
120 user_form = UserForm()()
121 try:
121 try:
122 form_result = user_form.to_python(dict(request.POST))
122 form_result = user_form.to_python(dict(request.POST))
123 user_model.create(form_result)
123 user_model.create(form_result)
124 usr = form_result['username']
124 usr = form_result['username']
125 action_logger(self.rhodecode_user, 'admin_created_user:%s' % usr,
125 action_logger(self.rhodecode_user, 'admin_created_user:%s' % usr,
126 None, self.ip_addr, self.sa)
126 None, self.ip_addr, self.sa)
127 h.flash(_('created user %s') % usr,
127 h.flash(_('created user %s') % usr,
128 category='success')
128 category='success')
129 Session().commit()
129 Session().commit()
130 except formencode.Invalid, errors:
130 except formencode.Invalid, errors:
131 return htmlfill.render(
131 return htmlfill.render(
132 render('admin/users/user_add.html'),
132 render('admin/users/user_add.html'),
133 defaults=errors.value,
133 defaults=errors.value,
134 errors=errors.error_dict or {},
134 errors=errors.error_dict or {},
135 prefix_error=False,
135 prefix_error=False,
136 encoding="UTF-8")
136 encoding="UTF-8")
137 except Exception:
137 except Exception:
138 log.error(traceback.format_exc())
138 log.error(traceback.format_exc())
139 h.flash(_('error occurred during creation of user %s') \
139 h.flash(_('error occurred during creation of user %s') \
140 % request.POST.get('username'), category='error')
140 % request.POST.get('username'), category='error')
141 return redirect(url('users'))
141 return redirect(url('users'))
142
142
143 def new(self, format='html'):
143 def new(self, format='html'):
144 """GET /users/new: Form to create a new item"""
144 """GET /users/new: Form to create a new item"""
145 # url('new_user')
145 # url('new_user')
146 return render('admin/users/user_add.html')
146 return render('admin/users/user_add.html')
147
147
148 def update(self, id):
148 def update(self, id):
149 """PUT /users/id: Update an existing item"""
149 """PUT /users/id: Update an existing item"""
150 # Forms posted to this method should contain a hidden field:
150 # Forms posted to this method should contain a hidden field:
151 # <input type="hidden" name="_method" value="PUT" />
151 # <input type="hidden" name="_method" value="PUT" />
152 # Or using helpers:
152 # Or using helpers:
153 # h.form(url('update_user', id=ID),
153 # h.form(url('update_user', id=ID),
154 # method='put')
154 # method='put')
155 # url('user', id=ID)
155 # url('user', id=ID)
156 user_model = UserModel()
156 user_model = UserModel()
157 c.user = user_model.get(id)
157 c.user = user_model.get(id)
158 c.perm_user = AuthUser(user_id=id)
158 c.perm_user = AuthUser(user_id=id)
159 _form = UserForm(edit=True, old_data={'user_id': id,
159 _form = UserForm(edit=True, old_data={'user_id': id,
160 'email': c.user.email})()
160 'email': c.user.email})()
161 form_result = {}
161 form_result = {}
162 try:
162 try:
163 form_result = _form.to_python(dict(request.POST))
163 form_result = _form.to_python(dict(request.POST))
164 user_model.update(id, form_result)
164 user_model.update(id, form_result)
165 usr = form_result['username']
165 usr = form_result['username']
166 action_logger(self.rhodecode_user, 'admin_updated_user:%s' % usr,
166 action_logger(self.rhodecode_user, 'admin_updated_user:%s' % usr,
167 None, self.ip_addr, self.sa)
167 None, self.ip_addr, self.sa)
168 h.flash(_('User updated successfully'), category='success')
168 h.flash(_('User updated successfully'), category='success')
169 Session().commit()
169 Session().commit()
170 except formencode.Invalid, errors:
170 except formencode.Invalid, errors:
171 c.user_email_map = UserEmailMap.query()\
171 c.user_email_map = UserEmailMap.query()\
172 .filter(UserEmailMap.user == c.user).all()
172 .filter(UserEmailMap.user == c.user).all()
173 defaults = errors.value
173 defaults = errors.value
174 e = errors.error_dict or {}
174 e = errors.error_dict or {}
175 perm = Permission.get_by_key('hg.create.repository')
175 perm = Permission.get_by_key('hg.create.repository')
176 defaults.update({'create_repo_perm': user_model.has_perm(id, perm)})
176 defaults.update({'create_repo_perm': user_model.has_perm(id, perm)})
177 defaults.update({'_method': 'put'})
177 defaults.update({'_method': 'put'})
178 return htmlfill.render(
178 return htmlfill.render(
179 render('admin/users/user_edit.html'),
179 render('admin/users/user_edit.html'),
180 defaults=defaults,
180 defaults=defaults,
181 errors=e,
181 errors=e,
182 prefix_error=False,
182 prefix_error=False,
183 encoding="UTF-8")
183 encoding="UTF-8")
184 except Exception:
184 except Exception:
185 log.error(traceback.format_exc())
185 log.error(traceback.format_exc())
186 h.flash(_('error occurred during update of user %s') \
186 h.flash(_('error occurred during update of user %s') \
187 % form_result.get('username'), category='error')
187 % form_result.get('username'), category='error')
188 return redirect(url('users'))
188 return redirect(url('users'))
189
189
190 def delete(self, id):
190 def delete(self, id):
191 """DELETE /users/id: Delete an existing item"""
191 """DELETE /users/id: Delete an existing item"""
192 # Forms posted to this method should contain a hidden field:
192 # Forms posted to this method should contain a hidden field:
193 # <input type="hidden" name="_method" value="DELETE" />
193 # <input type="hidden" name="_method" value="DELETE" />
194 # Or using helpers:
194 # Or using helpers:
195 # h.form(url('delete_user', id=ID),
195 # h.form(url('delete_user', id=ID),
196 # method='delete')
196 # method='delete')
197 # url('user', id=ID)
197 # url('user', id=ID)
198 user_model = UserModel()
198 user_model = UserModel()
199 try:
199 try:
200 user_model.delete(id)
200 user_model.delete(id)
201 Session().commit()
201 Session().commit()
202 h.flash(_('successfully deleted user'), category='success')
202 h.flash(_('successfully deleted user'), category='success')
203 except (UserOwnsReposException, DefaultUserException), e:
203 except (UserOwnsReposException, DefaultUserException), e:
204 h.flash(e, category='warning')
204 h.flash(e, category='warning')
205 except Exception:
205 except Exception:
206 log.error(traceback.format_exc())
206 log.error(traceback.format_exc())
207 h.flash(_('An error occurred during deletion of user'),
207 h.flash(_('An error occurred during deletion of user'),
208 category='error')
208 category='error')
209 return redirect(url('users'))
209 return redirect(url('users'))
210
210
211 def show(self, id, format='html'):
211 def show(self, id, format='html'):
212 """GET /users/id: Show a specific item"""
212 """GET /users/id: Show a specific item"""
213 # url('user', id=ID)
213 # url('user', id=ID)
214
214
215 def edit(self, id, format='html'):
215 def edit(self, id, format='html'):
216 """GET /users/id/edit: Form to edit an existing item"""
216 """GET /users/id/edit: Form to edit an existing item"""
217 # url('edit_user', id=ID)
217 # url('edit_user', id=ID)
218 c.user = User.get_or_404(id)
218 c.user = User.get_or_404(id)
219
219
220 if c.user.username == 'default':
220 if c.user.username == 'default':
221 h.flash(_("You can't edit this user"), category='warning')
221 h.flash(_("You can't edit this user"), category='warning')
222 return redirect(url('users'))
222 return redirect(url('users'))
223 c.perm_user = AuthUser(user_id=id)
223 c.perm_user = AuthUser(user_id=id)
224 c.user.permissions = {}
224 c.user.permissions = {}
225 c.granted_permissions = UserModel().fill_perms(c.user)\
225 c.granted_permissions = UserModel().fill_perms(c.user)\
226 .permissions['global']
226 .permissions['global']
227 c.user_email_map = UserEmailMap.query()\
227 c.user_email_map = UserEmailMap.query()\
228 .filter(UserEmailMap.user == c.user).all()
228 .filter(UserEmailMap.user == c.user).all()
229 defaults = c.user.get_dict()
229 defaults = c.user.get_dict()
230 perm = Permission.get_by_key('hg.create.repository')
230 perm = Permission.get_by_key('hg.create.repository')
231 defaults.update({'create_repo_perm': UserModel().has_perm(id, perm)})
231 defaults.update({'create_repo_perm': UserModel().has_perm(id, perm)})
232
232
233 return htmlfill.render(
233 return htmlfill.render(
234 render('admin/users/user_edit.html'),
234 render('admin/users/user_edit.html'),
235 defaults=defaults,
235 defaults=defaults,
236 encoding="UTF-8",
236 encoding="UTF-8",
237 force_defaults=False
237 force_defaults=False
238 )
238 )
239
239
240 def update_perm(self, id):
240 def update_perm(self, id):
241 """PUT /users_perm/id: Update an existing item"""
241 """PUT /users_perm/id: Update an existing item"""
242 # url('user_perm', id=ID, method='put')
242 # url('user_perm', id=ID, method='put')
243
243
244 grant_perm = request.POST.get('create_repo_perm', False)
244 grant_perm = request.POST.get('create_repo_perm', False)
245 user_model = UserModel()
245 user_model = UserModel()
246
246
247 if grant_perm:
247 if grant_perm:
248 perm = Permission.get_by_key('hg.create.none')
248 perm = Permission.get_by_key('hg.create.none')
249 user_model.revoke_perm(id, perm)
249 user_model.revoke_perm(id, perm)
250
250
251 perm = Permission.get_by_key('hg.create.repository')
251 perm = Permission.get_by_key('hg.create.repository')
252 user_model.grant_perm(id, perm)
252 user_model.grant_perm(id, perm)
253 h.flash(_("Granted 'repository create' permission to user"),
253 h.flash(_("Granted 'repository create' permission to user"),
254 category='success')
254 category='success')
255 Session().commit()
255 Session().commit()
256 else:
256 else:
257 perm = Permission.get_by_key('hg.create.repository')
257 perm = Permission.get_by_key('hg.create.repository')
258 user_model.revoke_perm(id, perm)
258 user_model.revoke_perm(id, perm)
259
259
260 perm = Permission.get_by_key('hg.create.none')
260 perm = Permission.get_by_key('hg.create.none')
261 user_model.grant_perm(id, perm)
261 user_model.grant_perm(id, perm)
262 h.flash(_("Revoked 'repository create' permission to user"),
262 h.flash(_("Revoked 'repository create' permission to user"),
263 category='success')
263 category='success')
264 Session().commit()
264 Session().commit()
265 return redirect(url('edit_user', id=id))
265 return redirect(url('edit_user', id=id))
266
266
267 def add_email(self, id):
267 def add_email(self, id):
268 """POST /user_emails:Add an existing item"""
268 """POST /user_emails:Add an existing item"""
269 # url('user_emails', id=ID, method='put')
269 # url('user_emails', id=ID, method='put')
270
270
271 #TODO: validation and form !!!
271 #TODO: validation and form !!!
272 email = request.POST.get('new_email')
272 email = request.POST.get('new_email')
273 user_model = UserModel()
273 user_model = UserModel()
274
274
275 try:
275 try:
276 user_model.add_extra_email(id, email)
276 user_model.add_extra_email(id, email)
277 Session().commit()
277 Session().commit()
278 h.flash(_("Added email %s to user") % email, category='success')
278 h.flash(_("Added email %s to user") % email, category='success')
279 except formencode.Invalid, error:
279 except formencode.Invalid, error:
280 msg = error.error_dict['email']
280 msg = error.error_dict['email']
281 h.flash(msg, category='error')
281 h.flash(msg, category='error')
282 except Exception:
282 except Exception:
283 log.error(traceback.format_exc())
283 log.error(traceback.format_exc())
284 h.flash(_('An error occurred during email saving'),
284 h.flash(_('An error occurred during email saving'),
285 category='error')
285 category='error')
286 return redirect(url('edit_user', id=id))
286 return redirect(url('edit_user', id=id))
287
287
288 def delete_email(self, id):
288 def delete_email(self, id):
289 """DELETE /user_emails_delete/id: Delete an existing item"""
289 """DELETE /user_emails_delete/id: Delete an existing item"""
290 # url('user_emails_delete', id=ID, method='delete')
290 # url('user_emails_delete', id=ID, method='delete')
291 user_model = UserModel()
291 user_model = UserModel()
292 user_model.delete_extra_email(id, request.POST.get('del_email'))
292 user_model.delete_extra_email(id, request.POST.get('del_email'))
293 Session().commit()
293 Session().commit()
294 h.flash(_("Removed email from user"), category='success')
294 h.flash(_("Removed email from user"), category='success')
295 return redirect(url('edit_user', id=id))
295 return redirect(url('edit_user', id=id))
@@ -1,251 +1,257 b''
1 """The base Controller API
1 """The base Controller API
2
2
3 Provides the BaseController class for subclassing.
3 Provides the BaseController class for subclassing.
4 """
4 """
5 import logging
5 import logging
6 import time
6 import time
7 import traceback
7 import traceback
8
8
9 from paste.auth.basic import AuthBasicAuthenticator
9 from paste.auth.basic import AuthBasicAuthenticator
10 from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden
10 from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden
11 from paste.httpheaders import WWW_AUTHENTICATE
11 from paste.httpheaders import WWW_AUTHENTICATE
12
12
13 from pylons import config, tmpl_context as c, request, session, url
13 from pylons import config, tmpl_context as c, request, session, url
14 from pylons.controllers import WSGIController
14 from pylons.controllers import WSGIController
15 from pylons.controllers.util import redirect
15 from pylons.controllers.util import redirect
16 from pylons.templating import render_mako as render
16 from pylons.templating import render_mako as render
17
17
18 from rhodecode import __version__, BACKENDS
18 from rhodecode import __version__, BACKENDS
19
19
20 from rhodecode.lib.utils2 import str2bool, safe_unicode
20 from rhodecode.lib.utils2 import str2bool, safe_unicode, AttributeDict
21 from rhodecode.lib.auth import AuthUser, get_container_username, authfunc,\
21 from rhodecode.lib.auth import AuthUser, get_container_username, authfunc,\
22 HasPermissionAnyMiddleware, CookieStoreWrapper
22 HasPermissionAnyMiddleware, CookieStoreWrapper
23 from rhodecode.lib.utils import get_repo_slug, invalidate_cache
23 from rhodecode.lib.utils import get_repo_slug, invalidate_cache
24 from rhodecode.model import meta
24 from rhodecode.model import meta
25
25
26 from rhodecode.model.db import Repository, RhodeCodeUi
26 from rhodecode.model.db import Repository, RhodeCodeUi
27 from rhodecode.model.notification import NotificationModel
27 from rhodecode.model.notification import NotificationModel
28 from rhodecode.model.scm import ScmModel
28 from rhodecode.model.scm import ScmModel
29
29
30 log = logging.getLogger(__name__)
30 log = logging.getLogger(__name__)
31
31
32
32
33 def _get_ip_addr(environ):
33 def _get_ip_addr(environ):
34 proxy_key = 'HTTP_X_REAL_IP'
34 proxy_key = 'HTTP_X_REAL_IP'
35 proxy_key2 = 'HTTP_X_FORWARDED_FOR'
35 proxy_key2 = 'HTTP_X_FORWARDED_FOR'
36 def_key = 'REMOTE_ADDR'
36 def_key = 'REMOTE_ADDR'
37
37
38 ip = environ.get(proxy_key2)
38 ip = environ.get(proxy_key2)
39 if ip:
39 if ip:
40 return ip
40 return ip
41
41
42 ip = environ.get(proxy_key)
42 ip = environ.get(proxy_key)
43
43
44 if ip:
44 if ip:
45 return ip
45 return ip
46
46
47 ip = environ.get(def_key, '0.0.0.0')
47 ip = environ.get(def_key, '0.0.0.0')
48 return ip
48 return ip
49
49
50
50
51 def _get_access_path(environ):
51 def _get_access_path(environ):
52 path = environ.get('PATH_INFO')
52 path = environ.get('PATH_INFO')
53 org_req = environ.get('pylons.original_request')
53 org_req = environ.get('pylons.original_request')
54 if org_req:
54 if org_req:
55 path = org_req.environ.get('PATH_INFO')
55 path = org_req.environ.get('PATH_INFO')
56 return path
56 return path
57
57
58
58
59 class BasicAuth(AuthBasicAuthenticator):
59 class BasicAuth(AuthBasicAuthenticator):
60
60
61 def __init__(self, realm, authfunc, auth_http_code=None):
61 def __init__(self, realm, authfunc, auth_http_code=None):
62 self.realm = realm
62 self.realm = realm
63 self.authfunc = authfunc
63 self.authfunc = authfunc
64 self._rc_auth_http_code = auth_http_code
64 self._rc_auth_http_code = auth_http_code
65
65
66 def build_authentication(self):
66 def build_authentication(self):
67 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
67 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
68 if self._rc_auth_http_code and self._rc_auth_http_code == '403':
68 if self._rc_auth_http_code and self._rc_auth_http_code == '403':
69 # return 403 if alternative http return code is specified in
69 # return 403 if alternative http return code is specified in
70 # RhodeCode config
70 # RhodeCode config
71 return HTTPForbidden(headers=head)
71 return HTTPForbidden(headers=head)
72 return HTTPUnauthorized(headers=head)
72 return HTTPUnauthorized(headers=head)
73
73
74
74
75 class BaseVCSController(object):
75 class BaseVCSController(object):
76
76
77 def __init__(self, application, config):
77 def __init__(self, application, config):
78 self.application = application
78 self.application = application
79 self.config = config
79 self.config = config
80 # base path of repo locations
80 # base path of repo locations
81 self.basepath = self.config['base_path']
81 self.basepath = self.config['base_path']
82 #authenticate this mercurial request using authfunc
82 #authenticate this mercurial request using authfunc
83 self.authenticate = BasicAuth('', authfunc,
83 self.authenticate = BasicAuth('', authfunc,
84 config.get('auth_ret_code'))
84 config.get('auth_ret_code'))
85 self.ipaddr = '0.0.0.0'
85 self.ipaddr = '0.0.0.0'
86
86
87 def _handle_request(self, environ, start_response):
87 def _handle_request(self, environ, start_response):
88 raise NotImplementedError()
88 raise NotImplementedError()
89
89
90 def _get_by_id(self, repo_name):
90 def _get_by_id(self, repo_name):
91 """
91 """
92 Get's a special pattern _<ID> from clone url and tries to replace it
92 Get's a special pattern _<ID> from clone url and tries to replace it
93 with a repository_name for support of _<ID> non changable urls
93 with a repository_name for support of _<ID> non changable urls
94
94
95 :param repo_name:
95 :param repo_name:
96 """
96 """
97 try:
97 try:
98 data = repo_name.split('/')
98 data = repo_name.split('/')
99 if len(data) >= 2:
99 if len(data) >= 2:
100 by_id = data[1].split('_')
100 by_id = data[1].split('_')
101 if len(by_id) == 2 and by_id[1].isdigit():
101 if len(by_id) == 2 and by_id[1].isdigit():
102 _repo_name = Repository.get(by_id[1]).repo_name
102 _repo_name = Repository.get(by_id[1]).repo_name
103 data[1] = _repo_name
103 data[1] = _repo_name
104 except:
104 except:
105 log.debug('Failed to extract repo_name from id %s' % (
105 log.debug('Failed to extract repo_name from id %s' % (
106 traceback.format_exc()
106 traceback.format_exc()
107 )
107 )
108 )
108 )
109
109
110 return '/'.join(data)
110 return '/'.join(data)
111
111
112 def _invalidate_cache(self, repo_name):
112 def _invalidate_cache(self, repo_name):
113 """
113 """
114 Set's cache for this repository for invalidation on next access
114 Set's cache for this repository for invalidation on next access
115
115
116 :param repo_name: full repo name, also a cache key
116 :param repo_name: full repo name, also a cache key
117 """
117 """
118 invalidate_cache('get_repo_cached_%s' % repo_name)
118 invalidate_cache('get_repo_cached_%s' % repo_name)
119
119
120 def _check_permission(self, action, user, repo_name):
120 def _check_permission(self, action, user, repo_name):
121 """
121 """
122 Checks permissions using action (push/pull) user and repository
122 Checks permissions using action (push/pull) user and repository
123 name
123 name
124
124
125 :param action: push or pull action
125 :param action: push or pull action
126 :param user: user instance
126 :param user: user instance
127 :param repo_name: repository name
127 :param repo_name: repository name
128 """
128 """
129 if action == 'push':
129 if action == 'push':
130 if not HasPermissionAnyMiddleware('repository.write',
130 if not HasPermissionAnyMiddleware('repository.write',
131 'repository.admin')(user,
131 'repository.admin')(user,
132 repo_name):
132 repo_name):
133 return False
133 return False
134
134
135 else:
135 else:
136 #any other action need at least read permission
136 #any other action need at least read permission
137 if not HasPermissionAnyMiddleware('repository.read',
137 if not HasPermissionAnyMiddleware('repository.read',
138 'repository.write',
138 'repository.write',
139 'repository.admin')(user,
139 'repository.admin')(user,
140 repo_name):
140 repo_name):
141 return False
141 return False
142
142
143 return True
143 return True
144
144
145 def _get_ip_addr(self, environ):
145 def _get_ip_addr(self, environ):
146 return _get_ip_addr(environ)
146 return _get_ip_addr(environ)
147
147
148 def _check_ssl(self, environ, start_response):
148 def _check_ssl(self, environ, start_response):
149 """
149 """
150 Checks the SSL check flag and returns False if SSL is not present
150 Checks the SSL check flag and returns False if SSL is not present
151 and required True otherwise
151 and required True otherwise
152 """
152 """
153 org_proto = environ['wsgi._org_proto']
153 org_proto = environ['wsgi._org_proto']
154 #check if we have SSL required ! if not it's a bad request !
154 #check if we have SSL required ! if not it's a bad request !
155 require_ssl = str2bool(RhodeCodeUi.get_by_key('push_ssl')\
155 require_ssl = str2bool(RhodeCodeUi.get_by_key('push_ssl')\
156 .scalar().ui_value)
156 .scalar().ui_value)
157 if require_ssl and org_proto == 'http':
157 if require_ssl and org_proto == 'http':
158 log.debug('proto is %s and SSL is required BAD REQUEST !'
158 log.debug('proto is %s and SSL is required BAD REQUEST !'
159 % org_proto)
159 % org_proto)
160 return False
160 return False
161 return True
161 return True
162
162
163 def __call__(self, environ, start_response):
163 def __call__(self, environ, start_response):
164 start = time.time()
164 start = time.time()
165 try:
165 try:
166 return self._handle_request(environ, start_response)
166 return self._handle_request(environ, start_response)
167 finally:
167 finally:
168 log = logging.getLogger('rhodecode.' + self.__class__.__name__)
168 log = logging.getLogger('rhodecode.' + self.__class__.__name__)
169 log.debug('Request time: %.3fs' % (time.time() - start))
169 log.debug('Request time: %.3fs' % (time.time() - start))
170 meta.Session.remove()
170 meta.Session.remove()
171
171
172
172
173 class BaseController(WSGIController):
173 class BaseController(WSGIController):
174
174
175 def __before__(self):
175 def __before__(self):
176 c.rhodecode_version = __version__
176 c.rhodecode_version = __version__
177 c.rhodecode_instanceid = config.get('instance_id')
177 c.rhodecode_instanceid = config.get('instance_id')
178 c.rhodecode_name = config.get('rhodecode_title')
178 c.rhodecode_name = config.get('rhodecode_title')
179 c.use_gravatar = str2bool(config.get('use_gravatar'))
179 c.use_gravatar = str2bool(config.get('use_gravatar'))
180 c.ga_code = config.get('rhodecode_ga_code')
180 c.ga_code = config.get('rhodecode_ga_code')
181 # Visual options
182 c.visual = AttributeDict({})
183 c.visual.show_public_icon = str2bool(config.get('rhodecode_show_public_icon'))
184 c.visual.show_private_icon = str2bool(config.get('rhodecode_show_private_icon'))
185 c.visual.stylify_metatags = str2bool(config.get('rhodecode_stylify_metatags'))
186
181 c.repo_name = get_repo_slug(request)
187 c.repo_name = get_repo_slug(request)
182 c.backends = BACKENDS.keys()
188 c.backends = BACKENDS.keys()
183 c.unread_notifications = NotificationModel()\
189 c.unread_notifications = NotificationModel()\
184 .get_unread_cnt_for_user(c.rhodecode_user.user_id)
190 .get_unread_cnt_for_user(c.rhodecode_user.user_id)
185 self.cut_off_limit = int(config.get('cut_off_limit'))
191 self.cut_off_limit = int(config.get('cut_off_limit'))
186
192
187 self.sa = meta.Session
193 self.sa = meta.Session
188 self.scm_model = ScmModel(self.sa)
194 self.scm_model = ScmModel(self.sa)
189 self.ip_addr = ''
195 self.ip_addr = ''
190
196
191 def __call__(self, environ, start_response):
197 def __call__(self, environ, start_response):
192 """Invoke the Controller"""
198 """Invoke the Controller"""
193 # WSGIController.__call__ dispatches to the Controller method
199 # WSGIController.__call__ dispatches to the Controller method
194 # the request is routed to. This routing information is
200 # the request is routed to. This routing information is
195 # available in environ['pylons.routes_dict']
201 # available in environ['pylons.routes_dict']
196 start = time.time()
202 start = time.time()
197 try:
203 try:
198 self.ip_addr = _get_ip_addr(environ)
204 self.ip_addr = _get_ip_addr(environ)
199 # make sure that we update permissions each time we call controller
205 # make sure that we update permissions each time we call controller
200 api_key = request.GET.get('api_key')
206 api_key = request.GET.get('api_key')
201 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
207 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
202 user_id = cookie_store.get('user_id', None)
208 user_id = cookie_store.get('user_id', None)
203 username = get_container_username(environ, config)
209 username = get_container_username(environ, config)
204 auth_user = AuthUser(user_id, api_key, username)
210 auth_user = AuthUser(user_id, api_key, username)
205 request.user = auth_user
211 request.user = auth_user
206 self.rhodecode_user = c.rhodecode_user = auth_user
212 self.rhodecode_user = c.rhodecode_user = auth_user
207 if not self.rhodecode_user.is_authenticated and \
213 if not self.rhodecode_user.is_authenticated and \
208 self.rhodecode_user.user_id is not None:
214 self.rhodecode_user.user_id is not None:
209 self.rhodecode_user.set_authenticated(
215 self.rhodecode_user.set_authenticated(
210 cookie_store.get('is_authenticated')
216 cookie_store.get('is_authenticated')
211 )
217 )
212 log.info('IP: %s User: %s accessed %s' % (
218 log.info('IP: %s User: %s accessed %s' % (
213 self.ip_addr, auth_user, safe_unicode(_get_access_path(environ)))
219 self.ip_addr, auth_user, safe_unicode(_get_access_path(environ)))
214 )
220 )
215 return WSGIController.__call__(self, environ, start_response)
221 return WSGIController.__call__(self, environ, start_response)
216 finally:
222 finally:
217 log.info('IP: %s Request to %s time: %.3fs' % (
223 log.info('IP: %s Request to %s time: %.3fs' % (
218 _get_ip_addr(environ),
224 _get_ip_addr(environ),
219 safe_unicode(_get_access_path(environ)), time.time() - start)
225 safe_unicode(_get_access_path(environ)), time.time() - start)
220 )
226 )
221 meta.Session.remove()
227 meta.Session.remove()
222
228
223
229
224 class BaseRepoController(BaseController):
230 class BaseRepoController(BaseController):
225 """
231 """
226 Base class for controllers responsible for loading all needed data for
232 Base class for controllers responsible for loading all needed data for
227 repository loaded items are
233 repository loaded items are
228
234
229 c.rhodecode_repo: instance of scm repository
235 c.rhodecode_repo: instance of scm repository
230 c.rhodecode_db_repo: instance of db
236 c.rhodecode_db_repo: instance of db
231 c.repository_followers: number of followers
237 c.repository_followers: number of followers
232 c.repository_forks: number of forks
238 c.repository_forks: number of forks
233 """
239 """
234
240
235 def __before__(self):
241 def __before__(self):
236 super(BaseRepoController, self).__before__()
242 super(BaseRepoController, self).__before__()
237 if c.repo_name:
243 if c.repo_name:
238
244
239 dbr = c.rhodecode_db_repo = Repository.get_by_repo_name(c.repo_name)
245 dbr = c.rhodecode_db_repo = Repository.get_by_repo_name(c.repo_name)
240 c.rhodecode_repo = c.rhodecode_db_repo.scm_instance
246 c.rhodecode_repo = c.rhodecode_db_repo.scm_instance
241
247
242 if c.rhodecode_repo is None:
248 if c.rhodecode_repo is None:
243 log.error('%s this repository is present in database but it '
249 log.error('%s this repository is present in database but it '
244 'cannot be created as an scm instance', c.repo_name)
250 'cannot be created as an scm instance', c.repo_name)
245
251
246 redirect(url('home'))
252 redirect(url('home'))
247
253
248 # some globals counter for menu
254 # some globals counter for menu
249 c.repository_followers = self.scm_model.get_followers(dbr)
255 c.repository_followers = self.scm_model.get_followers(dbr)
250 c.repository_forks = self.scm_model.get_forks(dbr)
256 c.repository_forks = self.scm_model.get_forks(dbr)
251 c.repository_pull_requests = self.scm_model.get_pull_requests(dbr)
257 c.repository_pull_requests = self.scm_model.get_pull_requests(dbr)
@@ -1,1022 +1,1043 b''
1 """Helper functions
1 """Helper functions
2
2
3 Consists of functions to typically be used within templates, but also
3 Consists of functions to typically be used within templates, but also
4 available to Controllers. This module is available to both as 'h'.
4 available to Controllers. This module is available to both as 'h'.
5 """
5 """
6 import random
6 import random
7 import hashlib
7 import hashlib
8 import StringIO
8 import StringIO
9 import urllib
9 import urllib
10 import math
10 import math
11 import logging
11 import logging
12 import re
12
13
13 from datetime import datetime
14 from datetime import datetime
14 from pygments.formatters.html import HtmlFormatter
15 from pygments.formatters.html import HtmlFormatter
15 from pygments import highlight as code_highlight
16 from pygments import highlight as code_highlight
16 from pylons import url, request, config
17 from pylons import url, request, config
17 from pylons.i18n.translation import _, ungettext
18 from pylons.i18n.translation import _, ungettext
18 from hashlib import md5
19 from hashlib import md5
19
20
20 from webhelpers.html import literal, HTML, escape
21 from webhelpers.html import literal, HTML, escape
21 from webhelpers.html.tools import *
22 from webhelpers.html.tools import *
22 from webhelpers.html.builder import make_tag
23 from webhelpers.html.builder import make_tag
23 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
24 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
24 end_form, file, form, hidden, image, javascript_link, link_to, \
25 end_form, file, form, hidden, image, javascript_link, link_to, \
25 link_to_if, link_to_unless, ol, required_legend, select, stylesheet_link, \
26 link_to_if, link_to_unless, ol, required_legend, select, stylesheet_link, \
26 submit, text, password, textarea, title, ul, xml_declaration, radio
27 submit, text, password, textarea, title, ul, xml_declaration, radio
27 from webhelpers.html.tools import auto_link, button_to, highlight, \
28 from webhelpers.html.tools import auto_link, button_to, highlight, \
28 js_obfuscate, mail_to, strip_links, strip_tags, tag_re
29 js_obfuscate, mail_to, strip_links, strip_tags, tag_re
29 from webhelpers.number import format_byte_size, format_bit_size
30 from webhelpers.number import format_byte_size, format_bit_size
30 from webhelpers.pylonslib import Flash as _Flash
31 from webhelpers.pylonslib import Flash as _Flash
31 from webhelpers.pylonslib.secure_form import secure_form
32 from webhelpers.pylonslib.secure_form import secure_form
32 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
33 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
33 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
34 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
34 replace_whitespace, urlify, truncate, wrap_paragraphs
35 replace_whitespace, urlify, truncate, wrap_paragraphs
35 from webhelpers.date import time_ago_in_words
36 from webhelpers.date import time_ago_in_words
36 from webhelpers.paginate import Page
37 from webhelpers.paginate import Page
37 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
38 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
38 convert_boolean_attrs, NotGiven, _make_safe_id_component
39 convert_boolean_attrs, NotGiven, _make_safe_id_component
39
40
40 from rhodecode.lib.annotate import annotate_highlight
41 from rhodecode.lib.annotate import annotate_highlight
41 from rhodecode.lib.utils import repo_name_slug
42 from rhodecode.lib.utils import repo_name_slug
42 from rhodecode.lib.utils2 import str2bool, safe_unicode, safe_str, \
43 from rhodecode.lib.utils2 import str2bool, safe_unicode, safe_str, \
43 get_changeset_safe
44 get_changeset_safe
44 from rhodecode.lib.markup_renderer import MarkupRenderer
45 from rhodecode.lib.markup_renderer import MarkupRenderer
45 from rhodecode.lib.vcs.exceptions import ChangesetDoesNotExistError
46 from rhodecode.lib.vcs.exceptions import ChangesetDoesNotExistError
46 from rhodecode.lib.vcs.backends.base import BaseChangeset
47 from rhodecode.lib.vcs.backends.base import BaseChangeset
47 from rhodecode.config.conf import DATE_FORMAT, DATETIME_FORMAT
48 from rhodecode.config.conf import DATE_FORMAT, DATETIME_FORMAT
48 from rhodecode.model.changeset_status import ChangesetStatusModel
49 from rhodecode.model.changeset_status import ChangesetStatusModel
49 from rhodecode.model.db import URL_SEP, Permission
50 from rhodecode.model.db import URL_SEP, Permission
50
51
51 log = logging.getLogger(__name__)
52 log = logging.getLogger(__name__)
52
53
53
54
54 html_escape_table = {
55 html_escape_table = {
55 "&": "&amp;",
56 "&": "&amp;",
56 '"': "&quot;",
57 '"': "&quot;",
57 "'": "&apos;",
58 "'": "&apos;",
58 ">": "&gt;",
59 ">": "&gt;",
59 "<": "&lt;",
60 "<": "&lt;",
60 }
61 }
61
62
62
63
63 def html_escape(text):
64 def html_escape(text):
64 """Produce entities within text."""
65 """Produce entities within text."""
65 return "".join(html_escape_table.get(c,c) for c in text)
66 return "".join(html_escape_table.get(c,c) for c in text)
66
67
67
68
68 def shorter(text, size=20):
69 def shorter(text, size=20):
69 postfix = '...'
70 postfix = '...'
70 if len(text) > size:
71 if len(text) > size:
71 return text[:size - len(postfix)] + postfix
72 return text[:size - len(postfix)] + postfix
72 return text
73 return text
73
74
74
75
75 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
76 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
76 """
77 """
77 Reset button
78 Reset button
78 """
79 """
79 _set_input_attrs(attrs, type, name, value)
80 _set_input_attrs(attrs, type, name, value)
80 _set_id_attr(attrs, id, name)
81 _set_id_attr(attrs, id, name)
81 convert_boolean_attrs(attrs, ["disabled"])
82 convert_boolean_attrs(attrs, ["disabled"])
82 return HTML.input(**attrs)
83 return HTML.input(**attrs)
83
84
84 reset = _reset
85 reset = _reset
85 safeid = _make_safe_id_component
86 safeid = _make_safe_id_component
86
87
87
88
88 def FID(raw_id, path):
89 def FID(raw_id, path):
89 """
90 """
90 Creates a uniqe ID for filenode based on it's hash of path and revision
91 Creates a uniqe ID for filenode based on it's hash of path and revision
91 it's safe to use in urls
92 it's safe to use in urls
92
93
93 :param raw_id:
94 :param raw_id:
94 :param path:
95 :param path:
95 """
96 """
96
97
97 return 'C-%s-%s' % (short_id(raw_id), md5(safe_str(path)).hexdigest()[:12])
98 return 'C-%s-%s' % (short_id(raw_id), md5(safe_str(path)).hexdigest()[:12])
98
99
99
100
100 def get_token():
101 def get_token():
101 """Return the current authentication token, creating one if one doesn't
102 """Return the current authentication token, creating one if one doesn't
102 already exist.
103 already exist.
103 """
104 """
104 token_key = "_authentication_token"
105 token_key = "_authentication_token"
105 from pylons import session
106 from pylons import session
106 if not token_key in session:
107 if not token_key in session:
107 try:
108 try:
108 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
109 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
109 except AttributeError: # Python < 2.4
110 except AttributeError: # Python < 2.4
110 token = hashlib.sha1(str(random.randrange(2 ** 128))).hexdigest()
111 token = hashlib.sha1(str(random.randrange(2 ** 128))).hexdigest()
111 session[token_key] = token
112 session[token_key] = token
112 if hasattr(session, 'save'):
113 if hasattr(session, 'save'):
113 session.save()
114 session.save()
114 return session[token_key]
115 return session[token_key]
115
116
116
117
117 class _GetError(object):
118 class _GetError(object):
118 """Get error from form_errors, and represent it as span wrapped error
119 """Get error from form_errors, and represent it as span wrapped error
119 message
120 message
120
121
121 :param field_name: field to fetch errors for
122 :param field_name: field to fetch errors for
122 :param form_errors: form errors dict
123 :param form_errors: form errors dict
123 """
124 """
124
125
125 def __call__(self, field_name, form_errors):
126 def __call__(self, field_name, form_errors):
126 tmpl = """<span class="error_msg">%s</span>"""
127 tmpl = """<span class="error_msg">%s</span>"""
127 if form_errors and field_name in form_errors:
128 if form_errors and field_name in form_errors:
128 return literal(tmpl % form_errors.get(field_name))
129 return literal(tmpl % form_errors.get(field_name))
129
130
130 get_error = _GetError()
131 get_error = _GetError()
131
132
132
133
133 class _ToolTip(object):
134 class _ToolTip(object):
134
135
135 def __call__(self, tooltip_title, trim_at=50):
136 def __call__(self, tooltip_title, trim_at=50):
136 """
137 """
137 Special function just to wrap our text into nice formatted
138 Special function just to wrap our text into nice formatted
138 autowrapped text
139 autowrapped text
139
140
140 :param tooltip_title:
141 :param tooltip_title:
141 """
142 """
142 tooltip_title = escape(tooltip_title)
143 tooltip_title = escape(tooltip_title)
143 tooltip_title = tooltip_title.replace('<', '&lt;').replace('>', '&gt;')
144 tooltip_title = tooltip_title.replace('<', '&lt;').replace('>', '&gt;')
144 return tooltip_title
145 return tooltip_title
145 tooltip = _ToolTip()
146 tooltip = _ToolTip()
146
147
147
148
148 class _FilesBreadCrumbs(object):
149 class _FilesBreadCrumbs(object):
149
150
150 def __call__(self, repo_name, rev, paths):
151 def __call__(self, repo_name, rev, paths):
151 if isinstance(paths, str):
152 if isinstance(paths, str):
152 paths = safe_unicode(paths)
153 paths = safe_unicode(paths)
153 url_l = [link_to(repo_name, url('files_home',
154 url_l = [link_to(repo_name, url('files_home',
154 repo_name=repo_name,
155 repo_name=repo_name,
155 revision=rev, f_path=''))]
156 revision=rev, f_path=''))]
156 paths_l = paths.split('/')
157 paths_l = paths.split('/')
157 for cnt, p in enumerate(paths_l):
158 for cnt, p in enumerate(paths_l):
158 if p != '':
159 if p != '':
159 url_l.append(link_to(p,
160 url_l.append(link_to(p,
160 url('files_home',
161 url('files_home',
161 repo_name=repo_name,
162 repo_name=repo_name,
162 revision=rev,
163 revision=rev,
163 f_path='/'.join(paths_l[:cnt + 1])
164 f_path='/'.join(paths_l[:cnt + 1])
164 )
165 )
165 )
166 )
166 )
167 )
167
168
168 return literal('/'.join(url_l))
169 return literal('/'.join(url_l))
169
170
170 files_breadcrumbs = _FilesBreadCrumbs()
171 files_breadcrumbs = _FilesBreadCrumbs()
171
172
172
173
173 class CodeHtmlFormatter(HtmlFormatter):
174 class CodeHtmlFormatter(HtmlFormatter):
174 """
175 """
175 My code Html Formatter for source codes
176 My code Html Formatter for source codes
176 """
177 """
177
178
178 def wrap(self, source, outfile):
179 def wrap(self, source, outfile):
179 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
180 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
180
181
181 def _wrap_code(self, source):
182 def _wrap_code(self, source):
182 for cnt, it in enumerate(source):
183 for cnt, it in enumerate(source):
183 i, t = it
184 i, t = it
184 t = '<div id="L%s">%s</div>' % (cnt + 1, t)
185 t = '<div id="L%s">%s</div>' % (cnt + 1, t)
185 yield i, t
186 yield i, t
186
187
187 def _wrap_tablelinenos(self, inner):
188 def _wrap_tablelinenos(self, inner):
188 dummyoutfile = StringIO.StringIO()
189 dummyoutfile = StringIO.StringIO()
189 lncount = 0
190 lncount = 0
190 for t, line in inner:
191 for t, line in inner:
191 if t:
192 if t:
192 lncount += 1
193 lncount += 1
193 dummyoutfile.write(line)
194 dummyoutfile.write(line)
194
195
195 fl = self.linenostart
196 fl = self.linenostart
196 mw = len(str(lncount + fl - 1))
197 mw = len(str(lncount + fl - 1))
197 sp = self.linenospecial
198 sp = self.linenospecial
198 st = self.linenostep
199 st = self.linenostep
199 la = self.lineanchors
200 la = self.lineanchors
200 aln = self.anchorlinenos
201 aln = self.anchorlinenos
201 nocls = self.noclasses
202 nocls = self.noclasses
202 if sp:
203 if sp:
203 lines = []
204 lines = []
204
205
205 for i in range(fl, fl + lncount):
206 for i in range(fl, fl + lncount):
206 if i % st == 0:
207 if i % st == 0:
207 if i % sp == 0:
208 if i % sp == 0:
208 if aln:
209 if aln:
209 lines.append('<a href="#%s%d" class="special">%*d</a>' %
210 lines.append('<a href="#%s%d" class="special">%*d</a>' %
210 (la, i, mw, i))
211 (la, i, mw, i))
211 else:
212 else:
212 lines.append('<span class="special">%*d</span>' % (mw, i))
213 lines.append('<span class="special">%*d</span>' % (mw, i))
213 else:
214 else:
214 if aln:
215 if aln:
215 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
216 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
216 else:
217 else:
217 lines.append('%*d' % (mw, i))
218 lines.append('%*d' % (mw, i))
218 else:
219 else:
219 lines.append('')
220 lines.append('')
220 ls = '\n'.join(lines)
221 ls = '\n'.join(lines)
221 else:
222 else:
222 lines = []
223 lines = []
223 for i in range(fl, fl + lncount):
224 for i in range(fl, fl + lncount):
224 if i % st == 0:
225 if i % st == 0:
225 if aln:
226 if aln:
226 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
227 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
227 else:
228 else:
228 lines.append('%*d' % (mw, i))
229 lines.append('%*d' % (mw, i))
229 else:
230 else:
230 lines.append('')
231 lines.append('')
231 ls = '\n'.join(lines)
232 ls = '\n'.join(lines)
232
233
233 # in case you wonder about the seemingly redundant <div> here: since the
234 # in case you wonder about the seemingly redundant <div> here: since the
234 # content in the other cell also is wrapped in a div, some browsers in
235 # content in the other cell also is wrapped in a div, some browsers in
235 # some configurations seem to mess up the formatting...
236 # some configurations seem to mess up the formatting...
236 if nocls:
237 if nocls:
237 yield 0, ('<table class="%stable">' % self.cssclass +
238 yield 0, ('<table class="%stable">' % self.cssclass +
238 '<tr><td><div class="linenodiv" '
239 '<tr><td><div class="linenodiv" '
239 'style="background-color: #f0f0f0; padding-right: 10px">'
240 'style="background-color: #f0f0f0; padding-right: 10px">'
240 '<pre style="line-height: 125%">' +
241 '<pre style="line-height: 125%">' +
241 ls + '</pre></div></td><td id="hlcode" class="code">')
242 ls + '</pre></div></td><td id="hlcode" class="code">')
242 else:
243 else:
243 yield 0, ('<table class="%stable">' % self.cssclass +
244 yield 0, ('<table class="%stable">' % self.cssclass +
244 '<tr><td class="linenos"><div class="linenodiv"><pre>' +
245 '<tr><td class="linenos"><div class="linenodiv"><pre>' +
245 ls + '</pre></div></td><td id="hlcode" class="code">')
246 ls + '</pre></div></td><td id="hlcode" class="code">')
246 yield 0, dummyoutfile.getvalue()
247 yield 0, dummyoutfile.getvalue()
247 yield 0, '</td></tr></table>'
248 yield 0, '</td></tr></table>'
248
249
249
250
250 def pygmentize(filenode, **kwargs):
251 def pygmentize(filenode, **kwargs):
251 """pygmentize function using pygments
252 """pygmentize function using pygments
252
253
253 :param filenode:
254 :param filenode:
254 """
255 """
255
256
256 return literal(code_highlight(filenode.content,
257 return literal(code_highlight(filenode.content,
257 filenode.lexer, CodeHtmlFormatter(**kwargs)))
258 filenode.lexer, CodeHtmlFormatter(**kwargs)))
258
259
259
260
260 def pygmentize_annotation(repo_name, filenode, **kwargs):
261 def pygmentize_annotation(repo_name, filenode, **kwargs):
261 """
262 """
262 pygmentize function for annotation
263 pygmentize function for annotation
263
264
264 :param filenode:
265 :param filenode:
265 """
266 """
266
267
267 color_dict = {}
268 color_dict = {}
268
269
269 def gen_color(n=10000):
270 def gen_color(n=10000):
270 """generator for getting n of evenly distributed colors using
271 """generator for getting n of evenly distributed colors using
271 hsv color and golden ratio. It always return same order of colors
272 hsv color and golden ratio. It always return same order of colors
272
273
273 :returns: RGB tuple
274 :returns: RGB tuple
274 """
275 """
275
276
276 def hsv_to_rgb(h, s, v):
277 def hsv_to_rgb(h, s, v):
277 if s == 0.0:
278 if s == 0.0:
278 return v, v, v
279 return v, v, v
279 i = int(h * 6.0) # XXX assume int() truncates!
280 i = int(h * 6.0) # XXX assume int() truncates!
280 f = (h * 6.0) - i
281 f = (h * 6.0) - i
281 p = v * (1.0 - s)
282 p = v * (1.0 - s)
282 q = v * (1.0 - s * f)
283 q = v * (1.0 - s * f)
283 t = v * (1.0 - s * (1.0 - f))
284 t = v * (1.0 - s * (1.0 - f))
284 i = i % 6
285 i = i % 6
285 if i == 0:
286 if i == 0:
286 return v, t, p
287 return v, t, p
287 if i == 1:
288 if i == 1:
288 return q, v, p
289 return q, v, p
289 if i == 2:
290 if i == 2:
290 return p, v, t
291 return p, v, t
291 if i == 3:
292 if i == 3:
292 return p, q, v
293 return p, q, v
293 if i == 4:
294 if i == 4:
294 return t, p, v
295 return t, p, v
295 if i == 5:
296 if i == 5:
296 return v, p, q
297 return v, p, q
297
298
298 golden_ratio = 0.618033988749895
299 golden_ratio = 0.618033988749895
299 h = 0.22717784590367374
300 h = 0.22717784590367374
300
301
301 for _ in xrange(n):
302 for _ in xrange(n):
302 h += golden_ratio
303 h += golden_ratio
303 h %= 1
304 h %= 1
304 HSV_tuple = [h, 0.95, 0.95]
305 HSV_tuple = [h, 0.95, 0.95]
305 RGB_tuple = hsv_to_rgb(*HSV_tuple)
306 RGB_tuple = hsv_to_rgb(*HSV_tuple)
306 yield map(lambda x: str(int(x * 256)), RGB_tuple)
307 yield map(lambda x: str(int(x * 256)), RGB_tuple)
307
308
308 cgenerator = gen_color()
309 cgenerator = gen_color()
309
310
310 def get_color_string(cs):
311 def get_color_string(cs):
311 if cs in color_dict:
312 if cs in color_dict:
312 col = color_dict[cs]
313 col = color_dict[cs]
313 else:
314 else:
314 col = color_dict[cs] = cgenerator.next()
315 col = color_dict[cs] = cgenerator.next()
315 return "color: rgb(%s)! important;" % (', '.join(col))
316 return "color: rgb(%s)! important;" % (', '.join(col))
316
317
317 def url_func(repo_name):
318 def url_func(repo_name):
318
319
319 def _url_func(changeset):
320 def _url_func(changeset):
320 author = changeset.author
321 author = changeset.author
321 date = changeset.date
322 date = changeset.date
322 message = tooltip(changeset.message)
323 message = tooltip(changeset.message)
323
324
324 tooltip_html = ("<div style='font-size:0.8em'><b>Author:</b>"
325 tooltip_html = ("<div style='font-size:0.8em'><b>Author:</b>"
325 " %s<br/><b>Date:</b> %s</b><br/><b>Message:"
326 " %s<br/><b>Date:</b> %s</b><br/><b>Message:"
326 "</b> %s<br/></div>")
327 "</b> %s<br/></div>")
327
328
328 tooltip_html = tooltip_html % (author, date, message)
329 tooltip_html = tooltip_html % (author, date, message)
329 lnk_format = '%5s:%s' % ('r%s' % changeset.revision,
330 lnk_format = '%5s:%s' % ('r%s' % changeset.revision,
330 short_id(changeset.raw_id))
331 short_id(changeset.raw_id))
331 uri = link_to(
332 uri = link_to(
332 lnk_format,
333 lnk_format,
333 url('changeset_home', repo_name=repo_name,
334 url('changeset_home', repo_name=repo_name,
334 revision=changeset.raw_id),
335 revision=changeset.raw_id),
335 style=get_color_string(changeset.raw_id),
336 style=get_color_string(changeset.raw_id),
336 class_='tooltip',
337 class_='tooltip',
337 title=tooltip_html
338 title=tooltip_html
338 )
339 )
339
340
340 uri += '\n'
341 uri += '\n'
341 return uri
342 return uri
342 return _url_func
343 return _url_func
343
344
344 return literal(annotate_highlight(filenode, url_func(repo_name), **kwargs))
345 return literal(annotate_highlight(filenode, url_func(repo_name), **kwargs))
345
346
346
347
347 def is_following_repo(repo_name, user_id):
348 def is_following_repo(repo_name, user_id):
348 from rhodecode.model.scm import ScmModel
349 from rhodecode.model.scm import ScmModel
349 return ScmModel().is_following_repo(repo_name, user_id)
350 return ScmModel().is_following_repo(repo_name, user_id)
350
351
351 flash = _Flash()
352 flash = _Flash()
352
353
353 #==============================================================================
354 #==============================================================================
354 # SCM FILTERS available via h.
355 # SCM FILTERS available via h.
355 #==============================================================================
356 #==============================================================================
356 from rhodecode.lib.vcs.utils import author_name, author_email
357 from rhodecode.lib.vcs.utils import author_name, author_email
357 from rhodecode.lib.utils2 import credentials_filter, age as _age
358 from rhodecode.lib.utils2 import credentials_filter, age as _age
358 from rhodecode.model.db import User, ChangesetStatus
359 from rhodecode.model.db import User, ChangesetStatus
359
360
360 age = lambda x: _age(x)
361 age = lambda x: _age(x)
361 capitalize = lambda x: x.capitalize()
362 capitalize = lambda x: x.capitalize()
362 email = author_email
363 email = author_email
363 short_id = lambda x: x[:12]
364 short_id = lambda x: x[:12]
364 hide_credentials = lambda x: ''.join(credentials_filter(x))
365 hide_credentials = lambda x: ''.join(credentials_filter(x))
365
366
366
367
367 def fmt_date(date):
368 def fmt_date(date):
368 if date:
369 if date:
369 _fmt = _(u"%a, %d %b %Y %H:%M:%S").encode('utf8')
370 _fmt = _(u"%a, %d %b %Y %H:%M:%S").encode('utf8')
370 return date.strftime(_fmt).decode('utf8')
371 return date.strftime(_fmt).decode('utf8')
371
372
372 return ""
373 return ""
373
374
374
375
375 def is_git(repository):
376 def is_git(repository):
376 if hasattr(repository, 'alias'):
377 if hasattr(repository, 'alias'):
377 _type = repository.alias
378 _type = repository.alias
378 elif hasattr(repository, 'repo_type'):
379 elif hasattr(repository, 'repo_type'):
379 _type = repository.repo_type
380 _type = repository.repo_type
380 else:
381 else:
381 _type = repository
382 _type = repository
382 return _type == 'git'
383 return _type == 'git'
383
384
384
385
385 def is_hg(repository):
386 def is_hg(repository):
386 if hasattr(repository, 'alias'):
387 if hasattr(repository, 'alias'):
387 _type = repository.alias
388 _type = repository.alias
388 elif hasattr(repository, 'repo_type'):
389 elif hasattr(repository, 'repo_type'):
389 _type = repository.repo_type
390 _type = repository.repo_type
390 else:
391 else:
391 _type = repository
392 _type = repository
392 return _type == 'hg'
393 return _type == 'hg'
393
394
394
395
395 def email_or_none(author):
396 def email_or_none(author):
396 _email = email(author)
397 _email = email(author)
397 if _email != '':
398 if _email != '':
398 return _email
399 return _email
399
400
400 # See if it contains a username we can get an email from
401 # See if it contains a username we can get an email from
401 user = User.get_by_username(author_name(author), case_insensitive=True,
402 user = User.get_by_username(author_name(author), case_insensitive=True,
402 cache=True)
403 cache=True)
403 if user is not None:
404 if user is not None:
404 return user.email
405 return user.email
405
406
406 # No valid email, not a valid user in the system, none!
407 # No valid email, not a valid user in the system, none!
407 return None
408 return None
408
409
409
410
410 def person(author):
411 def person(author):
411 # attr to return from fetched user
412 # attr to return from fetched user
412 person_getter = lambda usr: usr.username
413 person_getter = lambda usr: usr.username
413
414
414 # Valid email in the attribute passed, see if they're in the system
415 # Valid email in the attribute passed, see if they're in the system
415 _email = email(author)
416 _email = email(author)
416 if _email != '':
417 if _email != '':
417 user = User.get_by_email(_email, case_insensitive=True, cache=True)
418 user = User.get_by_email(_email, case_insensitive=True, cache=True)
418 if user is not None:
419 if user is not None:
419 return person_getter(user)
420 return person_getter(user)
420 return _email
421 return _email
421
422
422 # Maybe it's a username?
423 # Maybe it's a username?
423 _author = author_name(author)
424 _author = author_name(author)
424 user = User.get_by_username(_author, case_insensitive=True,
425 user = User.get_by_username(_author, case_insensitive=True,
425 cache=True)
426 cache=True)
426 if user is not None:
427 if user is not None:
427 return person_getter(user)
428 return person_getter(user)
428
429
429 # Still nothing? Just pass back the author name then
430 # Still nothing? Just pass back the author name then
430 return _author
431 return _author
431
432
432
433
434 def desc_stylize(value):
435 """
436 converts tags from value into html equivalent
437
438 :param value:
439 """
440 value = re.sub(r'\[see\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
441 '<div class="metatag" tag="see">see =&gt; \\1 </div>', value)
442 value = re.sub(r'\[license\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
443 '<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/\\1">\\1</a></div>', value)
444 value = re.sub(r'\[(requires|recommends|conflicts|base)\ \=\>\ *([a-zA-Z\-\/]*)\]',
445 '<div class="metatag" tag="\\1">\\1 =&gt; <a href="/\\2">\\2</a></div>', value)
446 value = re.sub(r'\[(lang|language)\ \=\>\ *([a-zA-Z\-\/]*)\]',
447 '<div class="metatag" tag="lang">\\2</div>', value)
448 value = re.sub(r'\[([a-z]+)\]',
449 '<div class="metatag" tag="\\1">\\1</div>', value)
450
451 return value
452
453
433 def bool2icon(value):
454 def bool2icon(value):
434 """Returns True/False values represented as small html image of true/false
455 """Returns True/False values represented as small html image of true/false
435 icons
456 icons
436
457
437 :param value: bool value
458 :param value: bool value
438 """
459 """
439
460
440 if value is True:
461 if value is True:
441 return HTML.tag('img', src=url("/images/icons/accept.png"),
462 return HTML.tag('img', src=url("/images/icons/accept.png"),
442 alt=_('True'))
463 alt=_('True'))
443
464
444 if value is False:
465 if value is False:
445 return HTML.tag('img', src=url("/images/icons/cancel.png"),
466 return HTML.tag('img', src=url("/images/icons/cancel.png"),
446 alt=_('False'))
467 alt=_('False'))
447
468
448 return value
469 return value
449
470
450
471
451 def action_parser(user_log, feed=False):
472 def action_parser(user_log, feed=False):
452 """
473 """
453 This helper will action_map the specified string action into translated
474 This helper will action_map the specified string action into translated
454 fancy names with icons and links
475 fancy names with icons and links
455
476
456 :param user_log: user log instance
477 :param user_log: user log instance
457 :param feed: use output for feeds (no html and fancy icons)
478 :param feed: use output for feeds (no html and fancy icons)
458 """
479 """
459
480
460 action = user_log.action
481 action = user_log.action
461 action_params = ' '
482 action_params = ' '
462
483
463 x = action.split(':')
484 x = action.split(':')
464
485
465 if len(x) > 1:
486 if len(x) > 1:
466 action, action_params = x
487 action, action_params = x
467
488
468 def get_cs_links():
489 def get_cs_links():
469 revs_limit = 3 # display this amount always
490 revs_limit = 3 # display this amount always
470 revs_top_limit = 50 # show upto this amount of changesets hidden
491 revs_top_limit = 50 # show upto this amount of changesets hidden
471 revs_ids = action_params.split(',')
492 revs_ids = action_params.split(',')
472 deleted = user_log.repository is None
493 deleted = user_log.repository is None
473 if deleted:
494 if deleted:
474 return ','.join(revs_ids)
495 return ','.join(revs_ids)
475
496
476 repo_name = user_log.repository.repo_name
497 repo_name = user_log.repository.repo_name
477
498
478 repo = user_log.repository.scm_instance
499 repo = user_log.repository.scm_instance
479
500
480 def lnk(rev, repo_name):
501 def lnk(rev, repo_name):
481
502
482 if isinstance(rev, BaseChangeset):
503 if isinstance(rev, BaseChangeset):
483 lbl = 'r%s:%s' % (rev.revision, rev.short_id)
504 lbl = 'r%s:%s' % (rev.revision, rev.short_id)
484 _url = url('changeset_home', repo_name=repo_name,
505 _url = url('changeset_home', repo_name=repo_name,
485 revision=rev.raw_id)
506 revision=rev.raw_id)
486 title = tooltip(rev.message)
507 title = tooltip(rev.message)
487 else:
508 else:
488 lbl = '%s' % rev
509 lbl = '%s' % rev
489 _url = '#'
510 _url = '#'
490 title = _('Changeset not found')
511 title = _('Changeset not found')
491
512
492 return link_to(lbl, _url, title=title, class_='tooltip',)
513 return link_to(lbl, _url, title=title, class_='tooltip',)
493
514
494 revs = []
515 revs = []
495 if len(filter(lambda v: v != '', revs_ids)) > 0:
516 if len(filter(lambda v: v != '', revs_ids)) > 0:
496 for rev in revs_ids[:revs_top_limit]:
517 for rev in revs_ids[:revs_top_limit]:
497 try:
518 try:
498 rev = repo.get_changeset(rev)
519 rev = repo.get_changeset(rev)
499 revs.append(rev)
520 revs.append(rev)
500 except ChangesetDoesNotExistError:
521 except ChangesetDoesNotExistError:
501 log.error('cannot find revision %s in this repo' % rev)
522 log.error('cannot find revision %s in this repo' % rev)
502 revs.append(rev)
523 revs.append(rev)
503 continue
524 continue
504 cs_links = []
525 cs_links = []
505 cs_links.append(" " + ', '.join(
526 cs_links.append(" " + ', '.join(
506 [lnk(rev, repo_name) for rev in revs[:revs_limit]]
527 [lnk(rev, repo_name) for rev in revs[:revs_limit]]
507 )
528 )
508 )
529 )
509
530
510 compare_view = (
531 compare_view = (
511 ' <div class="compare_view tooltip" title="%s">'
532 ' <div class="compare_view tooltip" title="%s">'
512 '<a href="%s">%s</a> </div>' % (
533 '<a href="%s">%s</a> </div>' % (
513 _('Show all combined changesets %s->%s') % (
534 _('Show all combined changesets %s->%s') % (
514 revs_ids[0], revs_ids[-1]
535 revs_ids[0], revs_ids[-1]
515 ),
536 ),
516 url('changeset_home', repo_name=repo_name,
537 url('changeset_home', repo_name=repo_name,
517 revision='%s...%s' % (revs_ids[0], revs_ids[-1])
538 revision='%s...%s' % (revs_ids[0], revs_ids[-1])
518 ),
539 ),
519 _('compare view')
540 _('compare view')
520 )
541 )
521 )
542 )
522
543
523 # if we have exactly one more than normally displayed
544 # if we have exactly one more than normally displayed
524 # just display it, takes less space than displaying
545 # just display it, takes less space than displaying
525 # "and 1 more revisions"
546 # "and 1 more revisions"
526 if len(revs_ids) == revs_limit + 1:
547 if len(revs_ids) == revs_limit + 1:
527 rev = revs[revs_limit]
548 rev = revs[revs_limit]
528 cs_links.append(", " + lnk(rev, repo_name))
549 cs_links.append(", " + lnk(rev, repo_name))
529
550
530 # hidden-by-default ones
551 # hidden-by-default ones
531 if len(revs_ids) > revs_limit + 1:
552 if len(revs_ids) > revs_limit + 1:
532 uniq_id = revs_ids[0]
553 uniq_id = revs_ids[0]
533 html_tmpl = (
554 html_tmpl = (
534 '<span> %s <a class="show_more" id="_%s" '
555 '<span> %s <a class="show_more" id="_%s" '
535 'href="#more">%s</a> %s</span>'
556 'href="#more">%s</a> %s</span>'
536 )
557 )
537 if not feed:
558 if not feed:
538 cs_links.append(html_tmpl % (
559 cs_links.append(html_tmpl % (
539 _('and'),
560 _('and'),
540 uniq_id, _('%s more') % (len(revs_ids) - revs_limit),
561 uniq_id, _('%s more') % (len(revs_ids) - revs_limit),
541 _('revisions')
562 _('revisions')
542 )
563 )
543 )
564 )
544
565
545 if not feed:
566 if not feed:
546 html_tmpl = '<span id="%s" style="display:none">, %s </span>'
567 html_tmpl = '<span id="%s" style="display:none">, %s </span>'
547 else:
568 else:
548 html_tmpl = '<span id="%s"> %s </span>'
569 html_tmpl = '<span id="%s"> %s </span>'
549
570
550 morelinks = ', '.join(
571 morelinks = ', '.join(
551 [lnk(rev, repo_name) for rev in revs[revs_limit:]]
572 [lnk(rev, repo_name) for rev in revs[revs_limit:]]
552 )
573 )
553
574
554 if len(revs_ids) > revs_top_limit:
575 if len(revs_ids) > revs_top_limit:
555 morelinks += ', ...'
576 morelinks += ', ...'
556
577
557 cs_links.append(html_tmpl % (uniq_id, morelinks))
578 cs_links.append(html_tmpl % (uniq_id, morelinks))
558 if len(revs) > 1:
579 if len(revs) > 1:
559 cs_links.append(compare_view)
580 cs_links.append(compare_view)
560 return ''.join(cs_links)
581 return ''.join(cs_links)
561
582
562 def get_fork_name():
583 def get_fork_name():
563 repo_name = action_params
584 repo_name = action_params
564 return _('fork name ') + str(link_to(action_params, url('summary_home',
585 return _('fork name ') + str(link_to(action_params, url('summary_home',
565 repo_name=repo_name,)))
586 repo_name=repo_name,)))
566
587
567 def get_user_name():
588 def get_user_name():
568 user_name = action_params
589 user_name = action_params
569 return user_name
590 return user_name
570
591
571 def get_users_group():
592 def get_users_group():
572 group_name = action_params
593 group_name = action_params
573 return group_name
594 return group_name
574
595
575 def get_pull_request():
596 def get_pull_request():
576 pull_request_id = action_params
597 pull_request_id = action_params
577 repo_name = user_log.repository.repo_name
598 repo_name = user_log.repository.repo_name
578 return link_to(_('Pull request #%s') % pull_request_id,
599 return link_to(_('Pull request #%s') % pull_request_id,
579 url('pullrequest_show', repo_name=repo_name,
600 url('pullrequest_show', repo_name=repo_name,
580 pull_request_id=pull_request_id))
601 pull_request_id=pull_request_id))
581
602
582 # action : translated str, callback(extractor), icon
603 # action : translated str, callback(extractor), icon
583 action_map = {
604 action_map = {
584 'user_deleted_repo': (_('[deleted] repository'),
605 'user_deleted_repo': (_('[deleted] repository'),
585 None, 'database_delete.png'),
606 None, 'database_delete.png'),
586 'user_created_repo': (_('[created] repository'),
607 'user_created_repo': (_('[created] repository'),
587 None, 'database_add.png'),
608 None, 'database_add.png'),
588 'user_created_fork': (_('[created] repository as fork'),
609 'user_created_fork': (_('[created] repository as fork'),
589 None, 'arrow_divide.png'),
610 None, 'arrow_divide.png'),
590 'user_forked_repo': (_('[forked] repository'),
611 'user_forked_repo': (_('[forked] repository'),
591 get_fork_name, 'arrow_divide.png'),
612 get_fork_name, 'arrow_divide.png'),
592 'user_updated_repo': (_('[updated] repository'),
613 'user_updated_repo': (_('[updated] repository'),
593 None, 'database_edit.png'),
614 None, 'database_edit.png'),
594 'admin_deleted_repo': (_('[delete] repository'),
615 'admin_deleted_repo': (_('[delete] repository'),
595 None, 'database_delete.png'),
616 None, 'database_delete.png'),
596 'admin_created_repo': (_('[created] repository'),
617 'admin_created_repo': (_('[created] repository'),
597 None, 'database_add.png'),
618 None, 'database_add.png'),
598 'admin_forked_repo': (_('[forked] repository'),
619 'admin_forked_repo': (_('[forked] repository'),
599 None, 'arrow_divide.png'),
620 None, 'arrow_divide.png'),
600 'admin_updated_repo': (_('[updated] repository'),
621 'admin_updated_repo': (_('[updated] repository'),
601 None, 'database_edit.png'),
622 None, 'database_edit.png'),
602 'admin_created_user': (_('[created] user'),
623 'admin_created_user': (_('[created] user'),
603 get_user_name, 'user_add.png'),
624 get_user_name, 'user_add.png'),
604 'admin_updated_user': (_('[updated] user'),
625 'admin_updated_user': (_('[updated] user'),
605 get_user_name, 'user_edit.png'),
626 get_user_name, 'user_edit.png'),
606 'admin_created_users_group': (_('[created] users group'),
627 'admin_created_users_group': (_('[created] users group'),
607 get_users_group, 'group_add.png'),
628 get_users_group, 'group_add.png'),
608 'admin_updated_users_group': (_('[updated] users group'),
629 'admin_updated_users_group': (_('[updated] users group'),
609 get_users_group, 'group_edit.png'),
630 get_users_group, 'group_edit.png'),
610 'user_commented_revision': (_('[commented] on revision in repository'),
631 'user_commented_revision': (_('[commented] on revision in repository'),
611 get_cs_links, 'comment_add.png'),
632 get_cs_links, 'comment_add.png'),
612 'user_commented_pull_request': (_('[commented] on pull request for'),
633 'user_commented_pull_request': (_('[commented] on pull request for'),
613 get_pull_request, 'comment_add.png'),
634 get_pull_request, 'comment_add.png'),
614 'user_closed_pull_request': (_('[closed] pull request for'),
635 'user_closed_pull_request': (_('[closed] pull request for'),
615 get_pull_request, 'tick.png'),
636 get_pull_request, 'tick.png'),
616 'push': (_('[pushed] into'),
637 'push': (_('[pushed] into'),
617 get_cs_links, 'script_add.png'),
638 get_cs_links, 'script_add.png'),
618 'push_local': (_('[committed via RhodeCode] into repository'),
639 'push_local': (_('[committed via RhodeCode] into repository'),
619 get_cs_links, 'script_edit.png'),
640 get_cs_links, 'script_edit.png'),
620 'push_remote': (_('[pulled from remote] into repository'),
641 'push_remote': (_('[pulled from remote] into repository'),
621 get_cs_links, 'connect.png'),
642 get_cs_links, 'connect.png'),
622 'pull': (_('[pulled] from'),
643 'pull': (_('[pulled] from'),
623 None, 'down_16.png'),
644 None, 'down_16.png'),
624 'started_following_repo': (_('[started following] repository'),
645 'started_following_repo': (_('[started following] repository'),
625 None, 'heart_add.png'),
646 None, 'heart_add.png'),
626 'stopped_following_repo': (_('[stopped following] repository'),
647 'stopped_following_repo': (_('[stopped following] repository'),
627 None, 'heart_delete.png'),
648 None, 'heart_delete.png'),
628 }
649 }
629
650
630 action_str = action_map.get(action, action)
651 action_str = action_map.get(action, action)
631 if feed:
652 if feed:
632 action = action_str[0].replace('[', '').replace(']', '')
653 action = action_str[0].replace('[', '').replace(']', '')
633 else:
654 else:
634 action = action_str[0]\
655 action = action_str[0]\
635 .replace('[', '<span class="journal_highlight">')\
656 .replace('[', '<span class="journal_highlight">')\
636 .replace(']', '</span>')
657 .replace(']', '</span>')
637
658
638 action_params_func = lambda: ""
659 action_params_func = lambda: ""
639
660
640 if callable(action_str[1]):
661 if callable(action_str[1]):
641 action_params_func = action_str[1]
662 action_params_func = action_str[1]
642
663
643 def action_parser_icon():
664 def action_parser_icon():
644 action = user_log.action
665 action = user_log.action
645 action_params = None
666 action_params = None
646 x = action.split(':')
667 x = action.split(':')
647
668
648 if len(x) > 1:
669 if len(x) > 1:
649 action, action_params = x
670 action, action_params = x
650
671
651 tmpl = """<img src="%s%s" alt="%s"/>"""
672 tmpl = """<img src="%s%s" alt="%s"/>"""
652 ico = action_map.get(action, ['', '', ''])[2]
673 ico = action_map.get(action, ['', '', ''])[2]
653 return literal(tmpl % ((url('/images/icons/')), ico, action))
674 return literal(tmpl % ((url('/images/icons/')), ico, action))
654
675
655 # returned callbacks we need to call to get
676 # returned callbacks we need to call to get
656 return [lambda: literal(action), action_params_func, action_parser_icon]
677 return [lambda: literal(action), action_params_func, action_parser_icon]
657
678
658
679
659
680
660 #==============================================================================
681 #==============================================================================
661 # PERMS
682 # PERMS
662 #==============================================================================
683 #==============================================================================
663 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
684 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
664 HasRepoPermissionAny, HasRepoPermissionAll
685 HasRepoPermissionAny, HasRepoPermissionAll
665
686
666
687
667 #==============================================================================
688 #==============================================================================
668 # GRAVATAR URL
689 # GRAVATAR URL
669 #==============================================================================
690 #==============================================================================
670
691
671 def gravatar_url(email_address, size=30):
692 def gravatar_url(email_address, size=30):
672 if (not str2bool(config['app_conf'].get('use_gravatar')) or
693 if (not str2bool(config['app_conf'].get('use_gravatar')) or
673 not email_address or email_address == 'anonymous@rhodecode.org'):
694 not email_address or email_address == 'anonymous@rhodecode.org'):
674 f = lambda a, l: min(l, key=lambda x: abs(x - a))
695 f = lambda a, l: min(l, key=lambda x: abs(x - a))
675 return url("/images/user%s.png" % f(size, [14, 16, 20, 24, 30]))
696 return url("/images/user%s.png" % f(size, [14, 16, 20, 24, 30]))
676
697
677 ssl_enabled = 'https' == request.environ.get('wsgi.url_scheme')
698 ssl_enabled = 'https' == request.environ.get('wsgi.url_scheme')
678 default = 'identicon'
699 default = 'identicon'
679 baseurl_nossl = "http://www.gravatar.com/avatar/"
700 baseurl_nossl = "http://www.gravatar.com/avatar/"
680 baseurl_ssl = "https://secure.gravatar.com/avatar/"
701 baseurl_ssl = "https://secure.gravatar.com/avatar/"
681 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
702 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
682
703
683 if isinstance(email_address, unicode):
704 if isinstance(email_address, unicode):
684 #hashlib crashes on unicode items
705 #hashlib crashes on unicode items
685 email_address = safe_str(email_address)
706 email_address = safe_str(email_address)
686 # construct the url
707 # construct the url
687 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
708 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
688 gravatar_url += urllib.urlencode({'d': default, 's': str(size)})
709 gravatar_url += urllib.urlencode({'d': default, 's': str(size)})
689
710
690 return gravatar_url
711 return gravatar_url
691
712
692
713
693 #==============================================================================
714 #==============================================================================
694 # REPO PAGER, PAGER FOR REPOSITORY
715 # REPO PAGER, PAGER FOR REPOSITORY
695 #==============================================================================
716 #==============================================================================
696 class RepoPage(Page):
717 class RepoPage(Page):
697
718
698 def __init__(self, collection, page=1, items_per_page=20,
719 def __init__(self, collection, page=1, items_per_page=20,
699 item_count=None, url=None, **kwargs):
720 item_count=None, url=None, **kwargs):
700
721
701 """Create a "RepoPage" instance. special pager for paging
722 """Create a "RepoPage" instance. special pager for paging
702 repository
723 repository
703 """
724 """
704 self._url_generator = url
725 self._url_generator = url
705
726
706 # Safe the kwargs class-wide so they can be used in the pager() method
727 # Safe the kwargs class-wide so they can be used in the pager() method
707 self.kwargs = kwargs
728 self.kwargs = kwargs
708
729
709 # Save a reference to the collection
730 # Save a reference to the collection
710 self.original_collection = collection
731 self.original_collection = collection
711
732
712 self.collection = collection
733 self.collection = collection
713
734
714 # The self.page is the number of the current page.
735 # The self.page is the number of the current page.
715 # The first page has the number 1!
736 # The first page has the number 1!
716 try:
737 try:
717 self.page = int(page) # make it int() if we get it as a string
738 self.page = int(page) # make it int() if we get it as a string
718 except (ValueError, TypeError):
739 except (ValueError, TypeError):
719 self.page = 1
740 self.page = 1
720
741
721 self.items_per_page = items_per_page
742 self.items_per_page = items_per_page
722
743
723 # Unless the user tells us how many items the collections has
744 # Unless the user tells us how many items the collections has
724 # we calculate that ourselves.
745 # we calculate that ourselves.
725 if item_count is not None:
746 if item_count is not None:
726 self.item_count = item_count
747 self.item_count = item_count
727 else:
748 else:
728 self.item_count = len(self.collection)
749 self.item_count = len(self.collection)
729
750
730 # Compute the number of the first and last available page
751 # Compute the number of the first and last available page
731 if self.item_count > 0:
752 if self.item_count > 0:
732 self.first_page = 1
753 self.first_page = 1
733 self.page_count = int(math.ceil(float(self.item_count) /
754 self.page_count = int(math.ceil(float(self.item_count) /
734 self.items_per_page))
755 self.items_per_page))
735 self.last_page = self.first_page + self.page_count - 1
756 self.last_page = self.first_page + self.page_count - 1
736
757
737 # Make sure that the requested page number is the range of
758 # Make sure that the requested page number is the range of
738 # valid pages
759 # valid pages
739 if self.page > self.last_page:
760 if self.page > self.last_page:
740 self.page = self.last_page
761 self.page = self.last_page
741 elif self.page < self.first_page:
762 elif self.page < self.first_page:
742 self.page = self.first_page
763 self.page = self.first_page
743
764
744 # Note: the number of items on this page can be less than
765 # Note: the number of items on this page can be less than
745 # items_per_page if the last page is not full
766 # items_per_page if the last page is not full
746 self.first_item = max(0, (self.item_count) - (self.page *
767 self.first_item = max(0, (self.item_count) - (self.page *
747 items_per_page))
768 items_per_page))
748 self.last_item = ((self.item_count - 1) - items_per_page *
769 self.last_item = ((self.item_count - 1) - items_per_page *
749 (self.page - 1))
770 (self.page - 1))
750
771
751 self.items = list(self.collection[self.first_item:self.last_item + 1])
772 self.items = list(self.collection[self.first_item:self.last_item + 1])
752
773
753 # Links to previous and next page
774 # Links to previous and next page
754 if self.page > self.first_page:
775 if self.page > self.first_page:
755 self.previous_page = self.page - 1
776 self.previous_page = self.page - 1
756 else:
777 else:
757 self.previous_page = None
778 self.previous_page = None
758
779
759 if self.page < self.last_page:
780 if self.page < self.last_page:
760 self.next_page = self.page + 1
781 self.next_page = self.page + 1
761 else:
782 else:
762 self.next_page = None
783 self.next_page = None
763
784
764 # No items available
785 # No items available
765 else:
786 else:
766 self.first_page = None
787 self.first_page = None
767 self.page_count = 0
788 self.page_count = 0
768 self.last_page = None
789 self.last_page = None
769 self.first_item = None
790 self.first_item = None
770 self.last_item = None
791 self.last_item = None
771 self.previous_page = None
792 self.previous_page = None
772 self.next_page = None
793 self.next_page = None
773 self.items = []
794 self.items = []
774
795
775 # This is a subclass of the 'list' type. Initialise the list now.
796 # This is a subclass of the 'list' type. Initialise the list now.
776 list.__init__(self, reversed(self.items))
797 list.__init__(self, reversed(self.items))
777
798
778
799
779 def changed_tooltip(nodes):
800 def changed_tooltip(nodes):
780 """
801 """
781 Generates a html string for changed nodes in changeset page.
802 Generates a html string for changed nodes in changeset page.
782 It limits the output to 30 entries
803 It limits the output to 30 entries
783
804
784 :param nodes: LazyNodesGenerator
805 :param nodes: LazyNodesGenerator
785 """
806 """
786 if nodes:
807 if nodes:
787 pref = ': <br/> '
808 pref = ': <br/> '
788 suf = ''
809 suf = ''
789 if len(nodes) > 30:
810 if len(nodes) > 30:
790 suf = '<br/>' + _(' and %s more') % (len(nodes) - 30)
811 suf = '<br/>' + _(' and %s more') % (len(nodes) - 30)
791 return literal(pref + '<br/> '.join([safe_unicode(x.path)
812 return literal(pref + '<br/> '.join([safe_unicode(x.path)
792 for x in nodes[:30]]) + suf)
813 for x in nodes[:30]]) + suf)
793 else:
814 else:
794 return ': ' + _('No Files')
815 return ': ' + _('No Files')
795
816
796
817
797 def repo_link(groups_and_repos):
818 def repo_link(groups_and_repos):
798 """
819 """
799 Makes a breadcrumbs link to repo within a group
820 Makes a breadcrumbs link to repo within a group
800 joins &raquo; on each group to create a fancy link
821 joins &raquo; on each group to create a fancy link
801
822
802 ex::
823 ex::
803 group >> subgroup >> repo
824 group >> subgroup >> repo
804
825
805 :param groups_and_repos:
826 :param groups_and_repos:
806 """
827 """
807 groups, repo_name = groups_and_repos
828 groups, repo_name = groups_and_repos
808
829
809 if not groups:
830 if not groups:
810 return repo_name
831 return repo_name
811 else:
832 else:
812 def make_link(group):
833 def make_link(group):
813 return link_to(group.name, url('repos_group_home',
834 return link_to(group.name, url('repos_group_home',
814 group_name=group.group_name))
835 group_name=group.group_name))
815 return literal(' &raquo; '.join(map(make_link, groups)) + \
836 return literal(' &raquo; '.join(map(make_link, groups)) + \
816 " &raquo; " + repo_name)
837 " &raquo; " + repo_name)
817
838
818
839
819 def fancy_file_stats(stats):
840 def fancy_file_stats(stats):
820 """
841 """
821 Displays a fancy two colored bar for number of added/deleted
842 Displays a fancy two colored bar for number of added/deleted
822 lines of code on file
843 lines of code on file
823
844
824 :param stats: two element list of added/deleted lines of code
845 :param stats: two element list of added/deleted lines of code
825 """
846 """
826
847
827 a, d, t = stats[0], stats[1], stats[0] + stats[1]
848 a, d, t = stats[0], stats[1], stats[0] + stats[1]
828 width = 100
849 width = 100
829 unit = float(width) / (t or 1)
850 unit = float(width) / (t or 1)
830
851
831 # needs > 9% of width to be visible or 0 to be hidden
852 # needs > 9% of width to be visible or 0 to be hidden
832 a_p = max(9, unit * a) if a > 0 else 0
853 a_p = max(9, unit * a) if a > 0 else 0
833 d_p = max(9, unit * d) if d > 0 else 0
854 d_p = max(9, unit * d) if d > 0 else 0
834 p_sum = a_p + d_p
855 p_sum = a_p + d_p
835
856
836 if p_sum > width:
857 if p_sum > width:
837 #adjust the percentage to be == 100% since we adjusted to 9
858 #adjust the percentage to be == 100% since we adjusted to 9
838 if a_p > d_p:
859 if a_p > d_p:
839 a_p = a_p - (p_sum - width)
860 a_p = a_p - (p_sum - width)
840 else:
861 else:
841 d_p = d_p - (p_sum - width)
862 d_p = d_p - (p_sum - width)
842
863
843 a_v = a if a > 0 else ''
864 a_v = a if a > 0 else ''
844 d_v = d if d > 0 else ''
865 d_v = d if d > 0 else ''
845
866
846 def cgen(l_type):
867 def cgen(l_type):
847 mapping = {'tr': 'top-right-rounded-corner-mid',
868 mapping = {'tr': 'top-right-rounded-corner-mid',
848 'tl': 'top-left-rounded-corner-mid',
869 'tl': 'top-left-rounded-corner-mid',
849 'br': 'bottom-right-rounded-corner-mid',
870 'br': 'bottom-right-rounded-corner-mid',
850 'bl': 'bottom-left-rounded-corner-mid'}
871 'bl': 'bottom-left-rounded-corner-mid'}
851 map_getter = lambda x: mapping[x]
872 map_getter = lambda x: mapping[x]
852
873
853 if l_type == 'a' and d_v:
874 if l_type == 'a' and d_v:
854 #case when added and deleted are present
875 #case when added and deleted are present
855 return ' '.join(map(map_getter, ['tl', 'bl']))
876 return ' '.join(map(map_getter, ['tl', 'bl']))
856
877
857 if l_type == 'a' and not d_v:
878 if l_type == 'a' and not d_v:
858 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
879 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
859
880
860 if l_type == 'd' and a_v:
881 if l_type == 'd' and a_v:
861 return ' '.join(map(map_getter, ['tr', 'br']))
882 return ' '.join(map(map_getter, ['tr', 'br']))
862
883
863 if l_type == 'd' and not a_v:
884 if l_type == 'd' and not a_v:
864 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
885 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
865
886
866 d_a = '<div class="added %s" style="width:%s%%">%s</div>' % (
887 d_a = '<div class="added %s" style="width:%s%%">%s</div>' % (
867 cgen('a'), a_p, a_v
888 cgen('a'), a_p, a_v
868 )
889 )
869 d_d = '<div class="deleted %s" style="width:%s%%">%s</div>' % (
890 d_d = '<div class="deleted %s" style="width:%s%%">%s</div>' % (
870 cgen('d'), d_p, d_v
891 cgen('d'), d_p, d_v
871 )
892 )
872 return literal('<div style="width:%spx">%s%s</div>' % (width, d_a, d_d))
893 return literal('<div style="width:%spx">%s%s</div>' % (width, d_a, d_d))
873
894
874
895
875 def urlify_text(text_):
896 def urlify_text(text_):
876 import re
897 import re
877
898
878 url_pat = re.compile(r'''(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]'''
899 url_pat = re.compile(r'''(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]'''
879 '''|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)''')
900 '''|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)''')
880
901
881 def url_func(match_obj):
902 def url_func(match_obj):
882 url_full = match_obj.groups()[0]
903 url_full = match_obj.groups()[0]
883 return '<a href="%(url)s">%(url)s</a>' % ({'url': url_full})
904 return '<a href="%(url)s">%(url)s</a>' % ({'url': url_full})
884
905
885 return literal(url_pat.sub(url_func, text_))
906 return literal(url_pat.sub(url_func, text_))
886
907
887
908
888 def urlify_changesets(text_, repository):
909 def urlify_changesets(text_, repository):
889 """
910 """
890 Extract revision ids from changeset and make link from them
911 Extract revision ids from changeset and make link from them
891
912
892 :param text_:
913 :param text_:
893 :param repository:
914 :param repository:
894 """
915 """
895 import re
916 import re
896 URL_PAT = re.compile(r'([0-9a-fA-F]{12,})')
917 URL_PAT = re.compile(r'([0-9a-fA-F]{12,})')
897
918
898 def url_func(match_obj):
919 def url_func(match_obj):
899 rev = match_obj.groups()[0]
920 rev = match_obj.groups()[0]
900 pref = ''
921 pref = ''
901 if match_obj.group().startswith(' '):
922 if match_obj.group().startswith(' '):
902 pref = ' '
923 pref = ' '
903 tmpl = (
924 tmpl = (
904 '%(pref)s<a class="%(cls)s" href="%(url)s">'
925 '%(pref)s<a class="%(cls)s" href="%(url)s">'
905 '%(rev)s'
926 '%(rev)s'
906 '</a>'
927 '</a>'
907 )
928 )
908 return tmpl % {
929 return tmpl % {
909 'pref': pref,
930 'pref': pref,
910 'cls': 'revision-link',
931 'cls': 'revision-link',
911 'url': url('changeset_home', repo_name=repository, revision=rev),
932 'url': url('changeset_home', repo_name=repository, revision=rev),
912 'rev': rev,
933 'rev': rev,
913 }
934 }
914
935
915 newtext = URL_PAT.sub(url_func, text_)
936 newtext = URL_PAT.sub(url_func, text_)
916
937
917 return newtext
938 return newtext
918
939
919
940
920 def urlify_commit(text_, repository=None, link_=None):
941 def urlify_commit(text_, repository=None, link_=None):
921 """
942 """
922 Parses given text message and makes proper links.
943 Parses given text message and makes proper links.
923 issues are linked to given issue-server, and rest is a changeset link
944 issues are linked to given issue-server, and rest is a changeset link
924 if link_ is given, in other case it's a plain text
945 if link_ is given, in other case it's a plain text
925
946
926 :param text_:
947 :param text_:
927 :param repository:
948 :param repository:
928 :param link_: changeset link
949 :param link_: changeset link
929 """
950 """
930 import re
951 import re
931 import traceback
952 import traceback
932
953
933 def escaper(string):
954 def escaper(string):
934 return string.replace('<', '&lt;').replace('>', '&gt;')
955 return string.replace('<', '&lt;').replace('>', '&gt;')
935
956
936 def linkify_others(t, l):
957 def linkify_others(t, l):
937 urls = re.compile(r'(\<a.*?\<\/a\>)',)
958 urls = re.compile(r'(\<a.*?\<\/a\>)',)
938 links = []
959 links = []
939 for e in urls.split(t):
960 for e in urls.split(t):
940 if not urls.match(e):
961 if not urls.match(e):
941 links.append('<a class="message-link" href="%s">%s</a>' % (l, e))
962 links.append('<a class="message-link" href="%s">%s</a>' % (l, e))
942 else:
963 else:
943 links.append(e)
964 links.append(e)
944
965
945 return ''.join(links)
966 return ''.join(links)
946
967
947 # urlify changesets - extrac revisions and make link out of them
968 # urlify changesets - extrac revisions and make link out of them
948 text_ = urlify_changesets(escaper(text_), repository)
969 text_ = urlify_changesets(escaper(text_), repository)
949
970
950 try:
971 try:
951 conf = config['app_conf']
972 conf = config['app_conf']
952
973
953 URL_PAT = re.compile(r'%s' % conf.get('issue_pat'))
974 URL_PAT = re.compile(r'%s' % conf.get('issue_pat'))
954
975
955 if URL_PAT:
976 if URL_PAT:
956 ISSUE_SERVER_LNK = conf.get('issue_server_link')
977 ISSUE_SERVER_LNK = conf.get('issue_server_link')
957 ISSUE_PREFIX = conf.get('issue_prefix')
978 ISSUE_PREFIX = conf.get('issue_prefix')
958
979
959 def url_func(match_obj):
980 def url_func(match_obj):
960 pref = ''
981 pref = ''
961 if match_obj.group().startswith(' '):
982 if match_obj.group().startswith(' '):
962 pref = ' '
983 pref = ' '
963
984
964 issue_id = ''.join(match_obj.groups())
985 issue_id = ''.join(match_obj.groups())
965 tmpl = (
986 tmpl = (
966 '%(pref)s<a class="%(cls)s" href="%(url)s">'
987 '%(pref)s<a class="%(cls)s" href="%(url)s">'
967 '%(issue-prefix)s%(id-repr)s'
988 '%(issue-prefix)s%(id-repr)s'
968 '</a>'
989 '</a>'
969 )
990 )
970 url = ISSUE_SERVER_LNK.replace('{id}', issue_id)
991 url = ISSUE_SERVER_LNK.replace('{id}', issue_id)
971 if repository:
992 if repository:
972 url = url.replace('{repo}', repository)
993 url = url.replace('{repo}', repository)
973 repo_name = repository.split(URL_SEP)[-1]
994 repo_name = repository.split(URL_SEP)[-1]
974 url = url.replace('{repo_name}', repo_name)
995 url = url.replace('{repo_name}', repo_name)
975 return tmpl % {
996 return tmpl % {
976 'pref': pref,
997 'pref': pref,
977 'cls': 'issue-tracker-link',
998 'cls': 'issue-tracker-link',
978 'url': url,
999 'url': url,
979 'id-repr': issue_id,
1000 'id-repr': issue_id,
980 'issue-prefix': ISSUE_PREFIX,
1001 'issue-prefix': ISSUE_PREFIX,
981 'serv': ISSUE_SERVER_LNK,
1002 'serv': ISSUE_SERVER_LNK,
982 }
1003 }
983
1004
984 newtext = URL_PAT.sub(url_func, text_)
1005 newtext = URL_PAT.sub(url_func, text_)
985
1006
986 if link_:
1007 if link_:
987 # wrap not links into final link => link_
1008 # wrap not links into final link => link_
988 newtext = linkify_others(newtext, link_)
1009 newtext = linkify_others(newtext, link_)
989
1010
990 return literal(newtext)
1011 return literal(newtext)
991 except:
1012 except:
992 log.error(traceback.format_exc())
1013 log.error(traceback.format_exc())
993 pass
1014 pass
994
1015
995 return text_
1016 return text_
996
1017
997
1018
998 def rst(source):
1019 def rst(source):
999 return literal('<div class="rst-block">%s</div>' %
1020 return literal('<div class="rst-block">%s</div>' %
1000 MarkupRenderer.rst(source))
1021 MarkupRenderer.rst(source))
1001
1022
1002
1023
1003 def rst_w_mentions(source):
1024 def rst_w_mentions(source):
1004 """
1025 """
1005 Wrapped rst renderer with @mention highlighting
1026 Wrapped rst renderer with @mention highlighting
1006
1027
1007 :param source:
1028 :param source:
1008 """
1029 """
1009 return literal('<div class="rst-block">%s</div>' %
1030 return literal('<div class="rst-block">%s</div>' %
1010 MarkupRenderer.rst_with_mentions(source))
1031 MarkupRenderer.rst_with_mentions(source))
1011
1032
1012
1033
1013 def changeset_status(repo, revision):
1034 def changeset_status(repo, revision):
1014 return ChangesetStatusModel().get_status(repo, revision)
1035 return ChangesetStatusModel().get_status(repo, revision)
1015
1036
1016
1037
1017 def changeset_status_lbl(changeset_status):
1038 def changeset_status_lbl(changeset_status):
1018 return dict(ChangesetStatus.STATUSES).get(changeset_status)
1039 return dict(ChangesetStatus.STATUSES).get(changeset_status)
1019
1040
1020
1041
1021 def get_permission_name(key):
1042 def get_permission_name(key):
1022 return dict(Permission.PERMS).get(key)
1043 return dict(Permission.PERMS).get(key)
@@ -1,445 +1,451 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.utils
3 rhodecode.lib.utils
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Some simple helper functions
6 Some simple helper functions
7
7
8 :created_on: Jan 5, 2011
8 :created_on: Jan 5, 2011
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2011-2012 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 re
26 import re
27 from datetime import datetime
27 from datetime import datetime
28 from pylons.i18n.translation import _, ungettext
28 from pylons.i18n.translation import _, ungettext
29 from rhodecode.lib.vcs.utils.lazy import LazyProperty
29 from rhodecode.lib.vcs.utils.lazy import LazyProperty
30
30
31
31
32 def __get_lem():
32 def __get_lem():
33 """
33 """
34 Get language extension map based on what's inside pygments lexers
34 Get language extension map based on what's inside pygments lexers
35 """
35 """
36 from pygments import lexers
36 from pygments import lexers
37 from string import lower
37 from string import lower
38 from collections import defaultdict
38 from collections import defaultdict
39
39
40 d = defaultdict(lambda: [])
40 d = defaultdict(lambda: [])
41
41
42 def __clean(s):
42 def __clean(s):
43 s = s.lstrip('*')
43 s = s.lstrip('*')
44 s = s.lstrip('.')
44 s = s.lstrip('.')
45
45
46 if s.find('[') != -1:
46 if s.find('[') != -1:
47 exts = []
47 exts = []
48 start, stop = s.find('['), s.find(']')
48 start, stop = s.find('['), s.find(']')
49
49
50 for suffix in s[start + 1:stop]:
50 for suffix in s[start + 1:stop]:
51 exts.append(s[:s.find('[')] + suffix)
51 exts.append(s[:s.find('[')] + suffix)
52 return map(lower, exts)
52 return map(lower, exts)
53 else:
53 else:
54 return map(lower, [s])
54 return map(lower, [s])
55
55
56 for lx, t in sorted(lexers.LEXERS.items()):
56 for lx, t in sorted(lexers.LEXERS.items()):
57 m = map(__clean, t[-2])
57 m = map(__clean, t[-2])
58 if m:
58 if m:
59 m = reduce(lambda x, y: x + y, m)
59 m = reduce(lambda x, y: x + y, m)
60 for ext in m:
60 for ext in m:
61 desc = lx.replace('Lexer', '')
61 desc = lx.replace('Lexer', '')
62 d[ext].append(desc)
62 d[ext].append(desc)
63
63
64 return dict(d)
64 return dict(d)
65
65
66 def str2bool(_str):
66 def str2bool(_str):
67 """
67 """
68 returs True/False value from given string, it tries to translate the
68 returs True/False value from given string, it tries to translate the
69 string into boolean
69 string into boolean
70
70
71 :param _str: string value to translate into boolean
71 :param _str: string value to translate into boolean
72 :rtype: boolean
72 :rtype: boolean
73 :returns: boolean from given string
73 :returns: boolean from given string
74 """
74 """
75 if _str is None:
75 if _str is None:
76 return False
76 return False
77 if _str in (True, False):
77 if _str in (True, False):
78 return _str
78 return _str
79 _str = str(_str).strip().lower()
79 _str = str(_str).strip().lower()
80 return _str in ('t', 'true', 'y', 'yes', 'on', '1')
80 return _str in ('t', 'true', 'y', 'yes', 'on', '1')
81
81
82
82
83 def convert_line_endings(line, mode):
83 def convert_line_endings(line, mode):
84 """
84 """
85 Converts a given line "line end" accordingly to given mode
85 Converts a given line "line end" accordingly to given mode
86
86
87 Available modes are::
87 Available modes are::
88 0 - Unix
88 0 - Unix
89 1 - Mac
89 1 - Mac
90 2 - DOS
90 2 - DOS
91
91
92 :param line: given line to convert
92 :param line: given line to convert
93 :param mode: mode to convert to
93 :param mode: mode to convert to
94 :rtype: str
94 :rtype: str
95 :return: converted line according to mode
95 :return: converted line according to mode
96 """
96 """
97 from string import replace
97 from string import replace
98
98
99 if mode == 0:
99 if mode == 0:
100 line = replace(line, '\r\n', '\n')
100 line = replace(line, '\r\n', '\n')
101 line = replace(line, '\r', '\n')
101 line = replace(line, '\r', '\n')
102 elif mode == 1:
102 elif mode == 1:
103 line = replace(line, '\r\n', '\r')
103 line = replace(line, '\r\n', '\r')
104 line = replace(line, '\n', '\r')
104 line = replace(line, '\n', '\r')
105 elif mode == 2:
105 elif mode == 2:
106 line = re.sub("\r(?!\n)|(?<!\r)\n", "\r\n", line)
106 line = re.sub("\r(?!\n)|(?<!\r)\n", "\r\n", line)
107 return line
107 return line
108
108
109
109
110 def detect_mode(line, default):
110 def detect_mode(line, default):
111 """
111 """
112 Detects line break for given line, if line break couldn't be found
112 Detects line break for given line, if line break couldn't be found
113 given default value is returned
113 given default value is returned
114
114
115 :param line: str line
115 :param line: str line
116 :param default: default
116 :param default: default
117 :rtype: int
117 :rtype: int
118 :return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS
118 :return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS
119 """
119 """
120 if line.endswith('\r\n'):
120 if line.endswith('\r\n'):
121 return 2
121 return 2
122 elif line.endswith('\n'):
122 elif line.endswith('\n'):
123 return 0
123 return 0
124 elif line.endswith('\r'):
124 elif line.endswith('\r'):
125 return 1
125 return 1
126 else:
126 else:
127 return default
127 return default
128
128
129
129
130 def generate_api_key(username, salt=None):
130 def generate_api_key(username, salt=None):
131 """
131 """
132 Generates unique API key for given username, if salt is not given
132 Generates unique API key for given username, if salt is not given
133 it'll be generated from some random string
133 it'll be generated from some random string
134
134
135 :param username: username as string
135 :param username: username as string
136 :param salt: salt to hash generate KEY
136 :param salt: salt to hash generate KEY
137 :rtype: str
137 :rtype: str
138 :returns: sha1 hash from username+salt
138 :returns: sha1 hash from username+salt
139 """
139 """
140 from tempfile import _RandomNameSequence
140 from tempfile import _RandomNameSequence
141 import hashlib
141 import hashlib
142
142
143 if salt is None:
143 if salt is None:
144 salt = _RandomNameSequence().next()
144 salt = _RandomNameSequence().next()
145
145
146 return hashlib.sha1(username + salt).hexdigest()
146 return hashlib.sha1(username + salt).hexdigest()
147
147
148
148
149 def safe_unicode(str_, from_encoding=None):
149 def safe_unicode(str_, from_encoding=None):
150 """
150 """
151 safe unicode function. Does few trick to turn str_ into unicode
151 safe unicode function. Does few trick to turn str_ into unicode
152
152
153 In case of UnicodeDecode error we try to return it with encoding detected
153 In case of UnicodeDecode error we try to return it with encoding detected
154 by chardet library if it fails fallback to unicode with errors replaced
154 by chardet library if it fails fallback to unicode with errors replaced
155
155
156 :param str_: string to decode
156 :param str_: string to decode
157 :rtype: unicode
157 :rtype: unicode
158 :returns: unicode object
158 :returns: unicode object
159 """
159 """
160 if isinstance(str_, unicode):
160 if isinstance(str_, unicode):
161 return str_
161 return str_
162
162
163 if not from_encoding:
163 if not from_encoding:
164 import rhodecode
164 import rhodecode
165 DEFAULT_ENCODING = rhodecode.CONFIG.get('default_encoding','utf8')
165 DEFAULT_ENCODING = rhodecode.CONFIG.get('default_encoding','utf8')
166 from_encoding = DEFAULT_ENCODING
166 from_encoding = DEFAULT_ENCODING
167
167
168 try:
168 try:
169 return unicode(str_)
169 return unicode(str_)
170 except UnicodeDecodeError:
170 except UnicodeDecodeError:
171 pass
171 pass
172
172
173 try:
173 try:
174 return unicode(str_, from_encoding)
174 return unicode(str_, from_encoding)
175 except UnicodeDecodeError:
175 except UnicodeDecodeError:
176 pass
176 pass
177
177
178 try:
178 try:
179 import chardet
179 import chardet
180 encoding = chardet.detect(str_)['encoding']
180 encoding = chardet.detect(str_)['encoding']
181 if encoding is None:
181 if encoding is None:
182 raise Exception()
182 raise Exception()
183 return str_.decode(encoding)
183 return str_.decode(encoding)
184 except (ImportError, UnicodeDecodeError, Exception):
184 except (ImportError, UnicodeDecodeError, Exception):
185 return unicode(str_, from_encoding, 'replace')
185 return unicode(str_, from_encoding, 'replace')
186
186
187
187
188 def safe_str(unicode_, to_encoding=None):
188 def safe_str(unicode_, to_encoding=None):
189 """
189 """
190 safe str function. Does few trick to turn unicode_ into string
190 safe str function. Does few trick to turn unicode_ into string
191
191
192 In case of UnicodeEncodeError we try to return it with encoding detected
192 In case of UnicodeEncodeError we try to return it with encoding detected
193 by chardet library if it fails fallback to string with errors replaced
193 by chardet library if it fails fallback to string with errors replaced
194
194
195 :param unicode_: unicode to encode
195 :param unicode_: unicode to encode
196 :rtype: str
196 :rtype: str
197 :returns: str object
197 :returns: str object
198 """
198 """
199
199
200 # if it's not basestr cast to str
200 # if it's not basestr cast to str
201 if not isinstance(unicode_, basestring):
201 if not isinstance(unicode_, basestring):
202 return str(unicode_)
202 return str(unicode_)
203
203
204 if isinstance(unicode_, str):
204 if isinstance(unicode_, str):
205 return unicode_
205 return unicode_
206
206
207 if not to_encoding:
207 if not to_encoding:
208 import rhodecode
208 import rhodecode
209 DEFAULT_ENCODING = rhodecode.CONFIG.get('default_encoding','utf8')
209 DEFAULT_ENCODING = rhodecode.CONFIG.get('default_encoding','utf8')
210 to_encoding = DEFAULT_ENCODING
210 to_encoding = DEFAULT_ENCODING
211
211
212 try:
212 try:
213 return unicode_.encode(to_encoding)
213 return unicode_.encode(to_encoding)
214 except UnicodeEncodeError:
214 except UnicodeEncodeError:
215 pass
215 pass
216
216
217 try:
217 try:
218 import chardet
218 import chardet
219 encoding = chardet.detect(unicode_)['encoding']
219 encoding = chardet.detect(unicode_)['encoding']
220 if encoding is None:
220 if encoding is None:
221 raise UnicodeEncodeError()
221 raise UnicodeEncodeError()
222
222
223 return unicode_.encode(encoding)
223 return unicode_.encode(encoding)
224 except (ImportError, UnicodeEncodeError):
224 except (ImportError, UnicodeEncodeError):
225 return unicode_.encode(to_encoding, 'replace')
225 return unicode_.encode(to_encoding, 'replace')
226
226
227 return safe_str
227 return safe_str
228
228
229
229
230 def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
230 def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
231 """
231 """
232 Custom engine_from_config functions that makes sure we use NullPool for
232 Custom engine_from_config functions that makes sure we use NullPool for
233 file based sqlite databases. This prevents errors on sqlite. This only
233 file based sqlite databases. This prevents errors on sqlite. This only
234 applies to sqlalchemy versions < 0.7.0
234 applies to sqlalchemy versions < 0.7.0
235
235
236 """
236 """
237 import sqlalchemy
237 import sqlalchemy
238 from sqlalchemy import engine_from_config as efc
238 from sqlalchemy import engine_from_config as efc
239 import logging
239 import logging
240
240
241 if int(sqlalchemy.__version__.split('.')[1]) < 7:
241 if int(sqlalchemy.__version__.split('.')[1]) < 7:
242
242
243 # This solution should work for sqlalchemy < 0.7.0, and should use
243 # This solution should work for sqlalchemy < 0.7.0, and should use
244 # proxy=TimerProxy() for execution time profiling
244 # proxy=TimerProxy() for execution time profiling
245
245
246 from sqlalchemy.pool import NullPool
246 from sqlalchemy.pool import NullPool
247 url = configuration[prefix + 'url']
247 url = configuration[prefix + 'url']
248
248
249 if url.startswith('sqlite'):
249 if url.startswith('sqlite'):
250 kwargs.update({'poolclass': NullPool})
250 kwargs.update({'poolclass': NullPool})
251 return efc(configuration, prefix, **kwargs)
251 return efc(configuration, prefix, **kwargs)
252 else:
252 else:
253 import time
253 import time
254 from sqlalchemy import event
254 from sqlalchemy import event
255 from sqlalchemy.engine import Engine
255 from sqlalchemy.engine import Engine
256
256
257 log = logging.getLogger('sqlalchemy.engine')
257 log = logging.getLogger('sqlalchemy.engine')
258 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38)
258 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38)
259 engine = efc(configuration, prefix, **kwargs)
259 engine = efc(configuration, prefix, **kwargs)
260
260
261 def color_sql(sql):
261 def color_sql(sql):
262 COLOR_SEQ = "\033[1;%dm"
262 COLOR_SEQ = "\033[1;%dm"
263 COLOR_SQL = YELLOW
263 COLOR_SQL = YELLOW
264 normal = '\x1b[0m'
264 normal = '\x1b[0m'
265 return ''.join([COLOR_SEQ % COLOR_SQL, sql, normal])
265 return ''.join([COLOR_SEQ % COLOR_SQL, sql, normal])
266
266
267 if configuration['debug']:
267 if configuration['debug']:
268 #attach events only for debug configuration
268 #attach events only for debug configuration
269
269
270 def before_cursor_execute(conn, cursor, statement,
270 def before_cursor_execute(conn, cursor, statement,
271 parameters, context, executemany):
271 parameters, context, executemany):
272 context._query_start_time = time.time()
272 context._query_start_time = time.time()
273 log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
273 log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
274
274
275
275
276 def after_cursor_execute(conn, cursor, statement,
276 def after_cursor_execute(conn, cursor, statement,
277 parameters, context, executemany):
277 parameters, context, executemany):
278 total = time.time() - context._query_start_time
278 total = time.time() - context._query_start_time
279 log.info(color_sql("<<<<< TOTAL TIME: %f <<<<<" % total))
279 log.info(color_sql("<<<<< TOTAL TIME: %f <<<<<" % total))
280
280
281 event.listen(engine, "before_cursor_execute",
281 event.listen(engine, "before_cursor_execute",
282 before_cursor_execute)
282 before_cursor_execute)
283 event.listen(engine, "after_cursor_execute",
283 event.listen(engine, "after_cursor_execute",
284 after_cursor_execute)
284 after_cursor_execute)
285
285
286 return engine
286 return engine
287
287
288
288
289 def age(prevdate):
289 def age(prevdate):
290 """
290 """
291 turns a datetime into an age string.
291 turns a datetime into an age string.
292
292
293 :param prevdate: datetime object
293 :param prevdate: datetime object
294 :rtype: unicode
294 :rtype: unicode
295 :returns: unicode words describing age
295 :returns: unicode words describing age
296 """
296 """
297
297
298 order = ['year', 'month', 'day', 'hour', 'minute', 'second']
298 order = ['year', 'month', 'day', 'hour', 'minute', 'second']
299 deltas = {}
299 deltas = {}
300
300
301 # Get date parts deltas
301 # Get date parts deltas
302 now = datetime.now()
302 now = datetime.now()
303 for part in order:
303 for part in order:
304 deltas[part] = getattr(now, part) - getattr(prevdate, part)
304 deltas[part] = getattr(now, part) - getattr(prevdate, part)
305
305
306 # Fix negative offsets (there is 1 second between 10:59:59 and 11:00:00,
306 # Fix negative offsets (there is 1 second between 10:59:59 and 11:00:00,
307 # not 1 hour, -59 minutes and -59 seconds)
307 # not 1 hour, -59 minutes and -59 seconds)
308
308
309 for num, length in [(5, 60), (4, 60), (3, 24)]: # seconds, minutes, hours
309 for num, length in [(5, 60), (4, 60), (3, 24)]: # seconds, minutes, hours
310 part = order[num]
310 part = order[num]
311 carry_part = order[num - 1]
311 carry_part = order[num - 1]
312
312
313 if deltas[part] < 0:
313 if deltas[part] < 0:
314 deltas[part] += length
314 deltas[part] += length
315 deltas[carry_part] -= 1
315 deltas[carry_part] -= 1
316
316
317 # Same thing for days except that the increment depends on the (variable)
317 # Same thing for days except that the increment depends on the (variable)
318 # number of days in the month
318 # number of days in the month
319 month_lengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
319 month_lengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
320 if deltas['day'] < 0:
320 if deltas['day'] < 0:
321 if prevdate.month == 2 and (prevdate.year % 4 == 0 and
321 if prevdate.month == 2 and (prevdate.year % 4 == 0 and
322 (prevdate.year % 100 != 0 or prevdate.year % 400 == 0)):
322 (prevdate.year % 100 != 0 or prevdate.year % 400 == 0)):
323 deltas['day'] += 29
323 deltas['day'] += 29
324 else:
324 else:
325 deltas['day'] += month_lengths[prevdate.month - 1]
325 deltas['day'] += month_lengths[prevdate.month - 1]
326
326
327 deltas['month'] -= 1
327 deltas['month'] -= 1
328
328
329 if deltas['month'] < 0:
329 if deltas['month'] < 0:
330 deltas['month'] += 12
330 deltas['month'] += 12
331 deltas['year'] -= 1
331 deltas['year'] -= 1
332
332
333 # Format the result
333 # Format the result
334 fmt_funcs = {
334 fmt_funcs = {
335 'year': lambda d: ungettext(u'%d year', '%d years', d) % d,
335 'year': lambda d: ungettext(u'%d year', '%d years', d) % d,
336 'month': lambda d: ungettext(u'%d month', '%d months', d) % d,
336 'month': lambda d: ungettext(u'%d month', '%d months', d) % d,
337 'day': lambda d: ungettext(u'%d day', '%d days', d) % d,
337 'day': lambda d: ungettext(u'%d day', '%d days', d) % d,
338 'hour': lambda d: ungettext(u'%d hour', '%d hours', d) % d,
338 'hour': lambda d: ungettext(u'%d hour', '%d hours', d) % d,
339 'minute': lambda d: ungettext(u'%d minute', '%d minutes', d) % d,
339 'minute': lambda d: ungettext(u'%d minute', '%d minutes', d) % d,
340 'second': lambda d: ungettext(u'%d second', '%d seconds', d) % d,
340 'second': lambda d: ungettext(u'%d second', '%d seconds', d) % d,
341 }
341 }
342
342
343 for i, part in enumerate(order):
343 for i, part in enumerate(order):
344 value = deltas[part]
344 value = deltas[part]
345 if value == 0:
345 if value == 0:
346 continue
346 continue
347
347
348 if i < 5:
348 if i < 5:
349 sub_part = order[i + 1]
349 sub_part = order[i + 1]
350 sub_value = deltas[sub_part]
350 sub_value = deltas[sub_part]
351 else:
351 else:
352 sub_value = 0
352 sub_value = 0
353
353
354 if sub_value == 0:
354 if sub_value == 0:
355 return _(u'%s ago') % fmt_funcs[part](value)
355 return _(u'%s ago') % fmt_funcs[part](value)
356
356
357 return _(u'%s and %s ago') % (fmt_funcs[part](value),
357 return _(u'%s and %s ago') % (fmt_funcs[part](value),
358 fmt_funcs[sub_part](sub_value))
358 fmt_funcs[sub_part](sub_value))
359
359
360 return _(u'just now')
360 return _(u'just now')
361
361
362
362
363 def uri_filter(uri):
363 def uri_filter(uri):
364 """
364 """
365 Removes user:password from given url string
365 Removes user:password from given url string
366
366
367 :param uri:
367 :param uri:
368 :rtype: unicode
368 :rtype: unicode
369 :returns: filtered list of strings
369 :returns: filtered list of strings
370 """
370 """
371 if not uri:
371 if not uri:
372 return ''
372 return ''
373
373
374 proto = ''
374 proto = ''
375
375
376 for pat in ('https://', 'http://'):
376 for pat in ('https://', 'http://'):
377 if uri.startswith(pat):
377 if uri.startswith(pat):
378 uri = uri[len(pat):]
378 uri = uri[len(pat):]
379 proto = pat
379 proto = pat
380 break
380 break
381
381
382 # remove passwords and username
382 # remove passwords and username
383 uri = uri[uri.find('@') + 1:]
383 uri = uri[uri.find('@') + 1:]
384
384
385 # get the port
385 # get the port
386 cred_pos = uri.find(':')
386 cred_pos = uri.find(':')
387 if cred_pos == -1:
387 if cred_pos == -1:
388 host, port = uri, None
388 host, port = uri, None
389 else:
389 else:
390 host, port = uri[:cred_pos], uri[cred_pos + 1:]
390 host, port = uri[:cred_pos], uri[cred_pos + 1:]
391
391
392 return filter(None, [proto, host, port])
392 return filter(None, [proto, host, port])
393
393
394
394
395 def credentials_filter(uri):
395 def credentials_filter(uri):
396 """
396 """
397 Returns a url with removed credentials
397 Returns a url with removed credentials
398
398
399 :param uri:
399 :param uri:
400 """
400 """
401
401
402 uri = uri_filter(uri)
402 uri = uri_filter(uri)
403 #check if we have port
403 #check if we have port
404 if len(uri) > 2 and uri[2]:
404 if len(uri) > 2 and uri[2]:
405 uri[2] = ':' + uri[2]
405 uri[2] = ':' + uri[2]
406
406
407 return ''.join(uri)
407 return ''.join(uri)
408
408
409
409
410 def get_changeset_safe(repo, rev):
410 def get_changeset_safe(repo, rev):
411 """
411 """
412 Safe version of get_changeset if this changeset doesn't exists for a
412 Safe version of get_changeset if this changeset doesn't exists for a
413 repo it returns a Dummy one instead
413 repo it returns a Dummy one instead
414
414
415 :param repo:
415 :param repo:
416 :param rev:
416 :param rev:
417 """
417 """
418 from rhodecode.lib.vcs.backends.base import BaseRepository
418 from rhodecode.lib.vcs.backends.base import BaseRepository
419 from rhodecode.lib.vcs.exceptions import RepositoryError
419 from rhodecode.lib.vcs.exceptions import RepositoryError
420 if not isinstance(repo, BaseRepository):
420 if not isinstance(repo, BaseRepository):
421 raise Exception('You must pass an Repository '
421 raise Exception('You must pass an Repository '
422 'object as first argument got %s', type(repo))
422 'object as first argument got %s', type(repo))
423
423
424 try:
424 try:
425 cs = repo.get_changeset(rev)
425 cs = repo.get_changeset(rev)
426 except RepositoryError:
426 except RepositoryError:
427 from rhodecode.lib.utils import EmptyChangeset
427 from rhodecode.lib.utils import EmptyChangeset
428 cs = EmptyChangeset(requested_revision=rev)
428 cs = EmptyChangeset(requested_revision=rev)
429 return cs
429 return cs
430
430
431
431
432 MENTIONS_REGEX = r'(?:^@|\s@)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+)(?:\s{1})'
432 MENTIONS_REGEX = r'(?:^@|\s@)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+)(?:\s{1})'
433
433
434
434
435 def extract_mentioned_users(s):
435 def extract_mentioned_users(s):
436 """
436 """
437 Returns unique usernames from given string s that have @mention
437 Returns unique usernames from given string s that have @mention
438
438
439 :param s: string to get mentions
439 :param s: string to get mentions
440 """
440 """
441 usrs = set()
441 usrs = set()
442 for username in re.findall(MENTIONS_REGEX, s):
442 for username in re.findall(MENTIONS_REGEX, s):
443 usrs.add(username)
443 usrs.add(username)
444
444
445 return sorted(list(usrs), key=lambda k: k.lower())
445 return sorted(list(usrs), key=lambda k: k.lower())
446
447 class AttributeDict(dict):
448 def __getattr__(self, attr):
449 return self.get(attr, None)
450 __setattr__ = dict.__setitem__
451 __delattr__ = dict.__delitem__
@@ -1,1672 +1,1679 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) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 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 import hashlib
30 import hashlib
31 from collections import defaultdict
31 from collections import defaultdict
32
32
33 from sqlalchemy import *
33 from sqlalchemy import *
34 from sqlalchemy.ext.hybrid import hybrid_property
34 from sqlalchemy.ext.hybrid import hybrid_property
35 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
35 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
36 from sqlalchemy.exc import DatabaseError
36 from sqlalchemy.exc import DatabaseError
37 from beaker.cache import cache_region, region_invalidate
37 from beaker.cache import cache_region, region_invalidate
38 from webob.exc import HTTPNotFound
38 from webob.exc import HTTPNotFound
39
39
40 from pylons.i18n.translation import lazy_ugettext as _
40 from pylons.i18n.translation import lazy_ugettext as _
41
41
42 from rhodecode.lib.vcs import get_backend
42 from rhodecode.lib.vcs import get_backend
43 from rhodecode.lib.vcs.utils.helpers import get_scm
43 from rhodecode.lib.vcs.utils.helpers import get_scm
44 from rhodecode.lib.vcs.exceptions import VCSError
44 from rhodecode.lib.vcs.exceptions import VCSError
45 from rhodecode.lib.vcs.utils.lazy import LazyProperty
45 from rhodecode.lib.vcs.utils.lazy import LazyProperty
46
46
47 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
47 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
48 safe_unicode
48 safe_unicode
49 from rhodecode.lib.compat import json
49 from rhodecode.lib.compat import json
50 from rhodecode.lib.caching_query import FromCache
50 from rhodecode.lib.caching_query import FromCache
51
51
52 from rhodecode.model.meta import Base, Session
52 from rhodecode.model.meta import Base, Session
53
53
54 URL_SEP = '/'
54 URL_SEP = '/'
55 log = logging.getLogger(__name__)
55 log = logging.getLogger(__name__)
56
56
57 #==============================================================================
57 #==============================================================================
58 # BASE CLASSES
58 # BASE CLASSES
59 #==============================================================================
59 #==============================================================================
60
60
61 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
61 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
62
62
63
63
64 class BaseModel(object):
64 class BaseModel(object):
65 """
65 """
66 Base Model for all classess
66 Base Model for all classess
67 """
67 """
68
68
69 @classmethod
69 @classmethod
70 def _get_keys(cls):
70 def _get_keys(cls):
71 """return column names for this model """
71 """return column names for this model """
72 return class_mapper(cls).c.keys()
72 return class_mapper(cls).c.keys()
73
73
74 def get_dict(self):
74 def get_dict(self):
75 """
75 """
76 return dict with keys and values corresponding
76 return dict with keys and values corresponding
77 to this model data """
77 to this model data """
78
78
79 d = {}
79 d = {}
80 for k in self._get_keys():
80 for k in self._get_keys():
81 d[k] = getattr(self, k)
81 d[k] = getattr(self, k)
82
82
83 # also use __json__() if present to get additional fields
83 # also use __json__() if present to get additional fields
84 _json_attr = getattr(self, '__json__', None)
84 _json_attr = getattr(self, '__json__', None)
85 if _json_attr:
85 if _json_attr:
86 # update with attributes from __json__
86 # update with attributes from __json__
87 if callable(_json_attr):
87 if callable(_json_attr):
88 _json_attr = _json_attr()
88 _json_attr = _json_attr()
89 for k, val in _json_attr.iteritems():
89 for k, val in _json_attr.iteritems():
90 d[k] = val
90 d[k] = val
91 return d
91 return d
92
92
93 def get_appstruct(self):
93 def get_appstruct(self):
94 """return list with keys and values tupples corresponding
94 """return list with keys and values tupples corresponding
95 to this model data """
95 to this model data """
96
96
97 l = []
97 l = []
98 for k in self._get_keys():
98 for k in self._get_keys():
99 l.append((k, getattr(self, k),))
99 l.append((k, getattr(self, k),))
100 return l
100 return l
101
101
102 def populate_obj(self, populate_dict):
102 def populate_obj(self, populate_dict):
103 """populate model with data from given populate_dict"""
103 """populate model with data from given populate_dict"""
104
104
105 for k in self._get_keys():
105 for k in self._get_keys():
106 if k in populate_dict:
106 if k in populate_dict:
107 setattr(self, k, populate_dict[k])
107 setattr(self, k, populate_dict[k])
108
108
109 @classmethod
109 @classmethod
110 def query(cls):
110 def query(cls):
111 return Session().query(cls)
111 return Session().query(cls)
112
112
113 @classmethod
113 @classmethod
114 def get(cls, id_):
114 def get(cls, id_):
115 if id_:
115 if id_:
116 return cls.query().get(id_)
116 return cls.query().get(id_)
117
117
118 @classmethod
118 @classmethod
119 def get_or_404(cls, id_):
119 def get_or_404(cls, id_):
120 if id_:
120 if id_:
121 res = cls.query().get(id_)
121 res = cls.query().get(id_)
122 if not res:
122 if not res:
123 raise HTTPNotFound
123 raise HTTPNotFound
124 return res
124 return res
125
125
126 @classmethod
126 @classmethod
127 def getAll(cls):
127 def getAll(cls):
128 return cls.query().all()
128 return cls.query().all()
129
129
130 @classmethod
130 @classmethod
131 def delete(cls, id_):
131 def delete(cls, id_):
132 obj = cls.query().get(id_)
132 obj = cls.query().get(id_)
133 Session().delete(obj)
133 Session().delete(obj)
134
134
135 def __repr__(self):
135 def __repr__(self):
136 if hasattr(self, '__unicode__'):
136 if hasattr(self, '__unicode__'):
137 # python repr needs to return str
137 # python repr needs to return str
138 return safe_str(self.__unicode__())
138 return safe_str(self.__unicode__())
139 return '<DB:%s>' % (self.__class__.__name__)
139 return '<DB:%s>' % (self.__class__.__name__)
140
140
141
141
142 class RhodeCodeSetting(Base, BaseModel):
142 class RhodeCodeSetting(Base, BaseModel):
143 __tablename__ = 'rhodecode_settings'
143 __tablename__ = 'rhodecode_settings'
144 __table_args__ = (
144 __table_args__ = (
145 UniqueConstraint('app_settings_name'),
145 UniqueConstraint('app_settings_name'),
146 {'extend_existing': True, 'mysql_engine': 'InnoDB',
146 {'extend_existing': True, 'mysql_engine': 'InnoDB',
147 'mysql_charset': 'utf8'}
147 'mysql_charset': 'utf8'}
148 )
148 )
149 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
149 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
150 app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
150 app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
151 _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
151 _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
152
152
153 def __init__(self, k='', v=''):
153 def __init__(self, k='', v=''):
154 self.app_settings_name = k
154 self.app_settings_name = k
155 self.app_settings_value = v
155 self.app_settings_value = v
156
156
157 @validates('_app_settings_value')
157 @validates('_app_settings_value')
158 def validate_settings_value(self, key, val):
158 def validate_settings_value(self, key, val):
159 assert type(val) == unicode
159 assert type(val) == unicode
160 return val
160 return val
161
161
162 @hybrid_property
162 @hybrid_property
163 def app_settings_value(self):
163 def app_settings_value(self):
164 v = self._app_settings_value
164 v = self._app_settings_value
165 if self.app_settings_name == 'ldap_active':
165 if self.app_settings_name == 'ldap_active':
166 v = str2bool(v)
166 v = str2bool(v)
167 return v
167 return v
168
168
169 @app_settings_value.setter
169 @app_settings_value.setter
170 def app_settings_value(self, val):
170 def app_settings_value(self, val):
171 """
171 """
172 Setter that will always make sure we use unicode in app_settings_value
172 Setter that will always make sure we use unicode in app_settings_value
173
173
174 :param val:
174 :param val:
175 """
175 """
176 self._app_settings_value = safe_unicode(val)
176 self._app_settings_value = safe_unicode(val)
177
177
178 def __unicode__(self):
178 def __unicode__(self):
179 return u"<%s('%s:%s')>" % (
179 return u"<%s('%s:%s')>" % (
180 self.__class__.__name__,
180 self.__class__.__name__,
181 self.app_settings_name, self.app_settings_value
181 self.app_settings_name, self.app_settings_value
182 )
182 )
183
183
184 @classmethod
184 @classmethod
185 def get_by_name(cls, ldap_key):
185 def get_by_name(cls, key):
186 return cls.query()\
186 return cls.query()\
187 .filter(cls.app_settings_name == ldap_key).scalar()
187 .filter(cls.app_settings_name == key).scalar()
188
189 @classmethod
190 def get_by_name_or_create(cls, key):
191 res = cls.get_by_name(key)
192 if not res:
193 res = cls(key)
194 return res
188
195
189 @classmethod
196 @classmethod
190 def get_app_settings(cls, cache=False):
197 def get_app_settings(cls, cache=False):
191
198
192 ret = cls.query()
199 ret = cls.query()
193
200
194 if cache:
201 if cache:
195 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
202 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
196
203
197 if not ret:
204 if not ret:
198 raise Exception('Could not get application settings !')
205 raise Exception('Could not get application settings !')
199 settings = {}
206 settings = {}
200 for each in ret:
207 for each in ret:
201 settings['rhodecode_' + each.app_settings_name] = \
208 settings['rhodecode_' + each.app_settings_name] = \
202 each.app_settings_value
209 each.app_settings_value
203
210
204 return settings
211 return settings
205
212
206 @classmethod
213 @classmethod
207 def get_ldap_settings(cls, cache=False):
214 def get_ldap_settings(cls, cache=False):
208 ret = cls.query()\
215 ret = cls.query()\
209 .filter(cls.app_settings_name.startswith('ldap_')).all()
216 .filter(cls.app_settings_name.startswith('ldap_')).all()
210 fd = {}
217 fd = {}
211 for row in ret:
218 for row in ret:
212 fd.update({row.app_settings_name: row.app_settings_value})
219 fd.update({row.app_settings_name: row.app_settings_value})
213
220
214 return fd
221 return fd
215
222
216
223
217 class RhodeCodeUi(Base, BaseModel):
224 class RhodeCodeUi(Base, BaseModel):
218 __tablename__ = 'rhodecode_ui'
225 __tablename__ = 'rhodecode_ui'
219 __table_args__ = (
226 __table_args__ = (
220 UniqueConstraint('ui_key'),
227 UniqueConstraint('ui_key'),
221 {'extend_existing': True, 'mysql_engine': 'InnoDB',
228 {'extend_existing': True, 'mysql_engine': 'InnoDB',
222 'mysql_charset': 'utf8'}
229 'mysql_charset': 'utf8'}
223 )
230 )
224
231
225 HOOK_UPDATE = 'changegroup.update'
232 HOOK_UPDATE = 'changegroup.update'
226 HOOK_REPO_SIZE = 'changegroup.repo_size'
233 HOOK_REPO_SIZE = 'changegroup.repo_size'
227 HOOK_PUSH = 'changegroup.push_logger'
234 HOOK_PUSH = 'changegroup.push_logger'
228 HOOK_PULL = 'preoutgoing.pull_logger'
235 HOOK_PULL = 'preoutgoing.pull_logger'
229
236
230 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
237 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
231 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
238 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
232 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
239 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
233 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
240 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
234 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
241 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
235
242
236 @classmethod
243 @classmethod
237 def get_by_key(cls, key):
244 def get_by_key(cls, key):
238 return cls.query().filter(cls.ui_key == key)
245 return cls.query().filter(cls.ui_key == key)
239
246
240 @classmethod
247 @classmethod
241 def get_builtin_hooks(cls):
248 def get_builtin_hooks(cls):
242 q = cls.query()
249 q = cls.query()
243 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
250 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
244 cls.HOOK_REPO_SIZE,
251 cls.HOOK_REPO_SIZE,
245 cls.HOOK_PUSH, cls.HOOK_PULL]))
252 cls.HOOK_PUSH, cls.HOOK_PULL]))
246 return q.all()
253 return q.all()
247
254
248 @classmethod
255 @classmethod
249 def get_custom_hooks(cls):
256 def get_custom_hooks(cls):
250 q = cls.query()
257 q = cls.query()
251 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
258 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
252 cls.HOOK_REPO_SIZE,
259 cls.HOOK_REPO_SIZE,
253 cls.HOOK_PUSH, cls.HOOK_PULL]))
260 cls.HOOK_PUSH, cls.HOOK_PULL]))
254 q = q.filter(cls.ui_section == 'hooks')
261 q = q.filter(cls.ui_section == 'hooks')
255 return q.all()
262 return q.all()
256
263
257 @classmethod
264 @classmethod
258 def get_repos_location(cls):
265 def get_repos_location(cls):
259 return cls.get_by_key('/').one().ui_value
266 return cls.get_by_key('/').one().ui_value
260
267
261 @classmethod
268 @classmethod
262 def create_or_update_hook(cls, key, val):
269 def create_or_update_hook(cls, key, val):
263 new_ui = cls.get_by_key(key).scalar() or cls()
270 new_ui = cls.get_by_key(key).scalar() or cls()
264 new_ui.ui_section = 'hooks'
271 new_ui.ui_section = 'hooks'
265 new_ui.ui_active = True
272 new_ui.ui_active = True
266 new_ui.ui_key = key
273 new_ui.ui_key = key
267 new_ui.ui_value = val
274 new_ui.ui_value = val
268
275
269 Session().add(new_ui)
276 Session().add(new_ui)
270
277
271
278
272 class User(Base, BaseModel):
279 class User(Base, BaseModel):
273 __tablename__ = 'users'
280 __tablename__ = 'users'
274 __table_args__ = (
281 __table_args__ = (
275 UniqueConstraint('username'), UniqueConstraint('email'),
282 UniqueConstraint('username'), UniqueConstraint('email'),
276 {'extend_existing': True, 'mysql_engine': 'InnoDB',
283 {'extend_existing': True, 'mysql_engine': 'InnoDB',
277 'mysql_charset': 'utf8'}
284 'mysql_charset': 'utf8'}
278 )
285 )
279 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
286 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
280 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
287 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
281 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
288 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
282 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
289 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
283 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
290 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
284 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
291 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
285 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
292 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
286 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
293 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
287 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
294 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
288 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
295 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
289 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
296 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
290
297
291 user_log = relationship('UserLog', cascade='all')
298 user_log = relationship('UserLog', cascade='all')
292 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
299 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
293
300
294 repositories = relationship('Repository')
301 repositories = relationship('Repository')
295 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
302 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
296 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
303 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
297 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
304 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
298
305
299 group_member = relationship('UsersGroupMember', cascade='all')
306 group_member = relationship('UsersGroupMember', cascade='all')
300
307
301 notifications = relationship('UserNotification', cascade='all')
308 notifications = relationship('UserNotification', cascade='all')
302 # notifications assigned to this user
309 # notifications assigned to this user
303 user_created_notifications = relationship('Notification', cascade='all')
310 user_created_notifications = relationship('Notification', cascade='all')
304 # comments created by this user
311 # comments created by this user
305 user_comments = relationship('ChangesetComment', cascade='all')
312 user_comments = relationship('ChangesetComment', cascade='all')
306 #extra emails for this user
313 #extra emails for this user
307 user_emails = relationship('UserEmailMap', cascade='all')
314 user_emails = relationship('UserEmailMap', cascade='all')
308
315
309 @hybrid_property
316 @hybrid_property
310 def email(self):
317 def email(self):
311 return self._email
318 return self._email
312
319
313 @email.setter
320 @email.setter
314 def email(self, val):
321 def email(self, val):
315 self._email = val.lower() if val else None
322 self._email = val.lower() if val else None
316
323
317 @property
324 @property
318 def emails(self):
325 def emails(self):
319 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
326 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
320 return [self.email] + [x.email for x in other]
327 return [self.email] + [x.email for x in other]
321
328
322 @property
329 @property
323 def full_name(self):
330 def full_name(self):
324 return '%s %s' % (self.name, self.lastname)
331 return '%s %s' % (self.name, self.lastname)
325
332
326 @property
333 @property
327 def full_name_or_username(self):
334 def full_name_or_username(self):
328 return ('%s %s' % (self.name, self.lastname)
335 return ('%s %s' % (self.name, self.lastname)
329 if (self.name and self.lastname) else self.username)
336 if (self.name and self.lastname) else self.username)
330
337
331 @property
338 @property
332 def full_contact(self):
339 def full_contact(self):
333 return '%s %s <%s>' % (self.name, self.lastname, self.email)
340 return '%s %s <%s>' % (self.name, self.lastname, self.email)
334
341
335 @property
342 @property
336 def short_contact(self):
343 def short_contact(self):
337 return '%s %s' % (self.name, self.lastname)
344 return '%s %s' % (self.name, self.lastname)
338
345
339 @property
346 @property
340 def is_admin(self):
347 def is_admin(self):
341 return self.admin
348 return self.admin
342
349
343 def __unicode__(self):
350 def __unicode__(self):
344 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
351 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
345 self.user_id, self.username)
352 self.user_id, self.username)
346
353
347 @classmethod
354 @classmethod
348 def get_by_username(cls, username, case_insensitive=False, cache=False):
355 def get_by_username(cls, username, case_insensitive=False, cache=False):
349 if case_insensitive:
356 if case_insensitive:
350 q = cls.query().filter(cls.username.ilike(username))
357 q = cls.query().filter(cls.username.ilike(username))
351 else:
358 else:
352 q = cls.query().filter(cls.username == username)
359 q = cls.query().filter(cls.username == username)
353
360
354 if cache:
361 if cache:
355 q = q.options(FromCache(
362 q = q.options(FromCache(
356 "sql_cache_short",
363 "sql_cache_short",
357 "get_user_%s" % _hash_key(username)
364 "get_user_%s" % _hash_key(username)
358 )
365 )
359 )
366 )
360 return q.scalar()
367 return q.scalar()
361
368
362 @classmethod
369 @classmethod
363 def get_by_api_key(cls, api_key, cache=False):
370 def get_by_api_key(cls, api_key, cache=False):
364 q = cls.query().filter(cls.api_key == api_key)
371 q = cls.query().filter(cls.api_key == api_key)
365
372
366 if cache:
373 if cache:
367 q = q.options(FromCache("sql_cache_short",
374 q = q.options(FromCache("sql_cache_short",
368 "get_api_key_%s" % api_key))
375 "get_api_key_%s" % api_key))
369 return q.scalar()
376 return q.scalar()
370
377
371 @classmethod
378 @classmethod
372 def get_by_email(cls, email, case_insensitive=False, cache=False):
379 def get_by_email(cls, email, case_insensitive=False, cache=False):
373 if case_insensitive:
380 if case_insensitive:
374 q = cls.query().filter(cls.email.ilike(email))
381 q = cls.query().filter(cls.email.ilike(email))
375 else:
382 else:
376 q = cls.query().filter(cls.email == email)
383 q = cls.query().filter(cls.email == email)
377
384
378 if cache:
385 if cache:
379 q = q.options(FromCache("sql_cache_short",
386 q = q.options(FromCache("sql_cache_short",
380 "get_email_key_%s" % email))
387 "get_email_key_%s" % email))
381
388
382 ret = q.scalar()
389 ret = q.scalar()
383 if ret is None:
390 if ret is None:
384 q = UserEmailMap.query()
391 q = UserEmailMap.query()
385 # try fetching in alternate email map
392 # try fetching in alternate email map
386 if case_insensitive:
393 if case_insensitive:
387 q = q.filter(UserEmailMap.email.ilike(email))
394 q = q.filter(UserEmailMap.email.ilike(email))
388 else:
395 else:
389 q = q.filter(UserEmailMap.email == email)
396 q = q.filter(UserEmailMap.email == email)
390 q = q.options(joinedload(UserEmailMap.user))
397 q = q.options(joinedload(UserEmailMap.user))
391 if cache:
398 if cache:
392 q = q.options(FromCache("sql_cache_short",
399 q = q.options(FromCache("sql_cache_short",
393 "get_email_map_key_%s" % email))
400 "get_email_map_key_%s" % email))
394 ret = getattr(q.scalar(), 'user', None)
401 ret = getattr(q.scalar(), 'user', None)
395
402
396 return ret
403 return ret
397
404
398 def update_lastlogin(self):
405 def update_lastlogin(self):
399 """Update user lastlogin"""
406 """Update user lastlogin"""
400 self.last_login = datetime.datetime.now()
407 self.last_login = datetime.datetime.now()
401 Session().add(self)
408 Session().add(self)
402 log.debug('updated user %s lastlogin' % self.username)
409 log.debug('updated user %s lastlogin' % self.username)
403
410
404 def get_api_data(self):
411 def get_api_data(self):
405 """
412 """
406 Common function for generating user related data for API
413 Common function for generating user related data for API
407 """
414 """
408 user = self
415 user = self
409 data = dict(
416 data = dict(
410 user_id=user.user_id,
417 user_id=user.user_id,
411 username=user.username,
418 username=user.username,
412 firstname=user.name,
419 firstname=user.name,
413 lastname=user.lastname,
420 lastname=user.lastname,
414 email=user.email,
421 email=user.email,
415 emails=user.emails,
422 emails=user.emails,
416 api_key=user.api_key,
423 api_key=user.api_key,
417 active=user.active,
424 active=user.active,
418 admin=user.admin,
425 admin=user.admin,
419 ldap_dn=user.ldap_dn,
426 ldap_dn=user.ldap_dn,
420 last_login=user.last_login,
427 last_login=user.last_login,
421 )
428 )
422 return data
429 return data
423
430
424 def __json__(self):
431 def __json__(self):
425 data = dict(
432 data = dict(
426 full_name=self.full_name,
433 full_name=self.full_name,
427 full_name_or_username=self.full_name_or_username,
434 full_name_or_username=self.full_name_or_username,
428 short_contact=self.short_contact,
435 short_contact=self.short_contact,
429 full_contact=self.full_contact
436 full_contact=self.full_contact
430 )
437 )
431 data.update(self.get_api_data())
438 data.update(self.get_api_data())
432 return data
439 return data
433
440
434
441
435 class UserEmailMap(Base, BaseModel):
442 class UserEmailMap(Base, BaseModel):
436 __tablename__ = 'user_email_map'
443 __tablename__ = 'user_email_map'
437 __table_args__ = (
444 __table_args__ = (
438 Index('uem_email_idx', 'email'),
445 Index('uem_email_idx', 'email'),
439 UniqueConstraint('email'),
446 UniqueConstraint('email'),
440 {'extend_existing': True, 'mysql_engine': 'InnoDB',
447 {'extend_existing': True, 'mysql_engine': 'InnoDB',
441 'mysql_charset': 'utf8'}
448 'mysql_charset': 'utf8'}
442 )
449 )
443 __mapper_args__ = {}
450 __mapper_args__ = {}
444
451
445 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
452 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
446 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
453 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
447 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
454 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
448
455
449 user = relationship('User', lazy='joined')
456 user = relationship('User', lazy='joined')
450
457
451 @validates('_email')
458 @validates('_email')
452 def validate_email(self, key, email):
459 def validate_email(self, key, email):
453 # check if this email is not main one
460 # check if this email is not main one
454 main_email = Session().query(User).filter(User.email == email).scalar()
461 main_email = Session().query(User).filter(User.email == email).scalar()
455 if main_email is not None:
462 if main_email is not None:
456 raise AttributeError('email %s is present is user table' % email)
463 raise AttributeError('email %s is present is user table' % email)
457 return email
464 return email
458
465
459 @hybrid_property
466 @hybrid_property
460 def email(self):
467 def email(self):
461 return self._email
468 return self._email
462
469
463 @email.setter
470 @email.setter
464 def email(self, val):
471 def email(self, val):
465 self._email = val.lower() if val else None
472 self._email = val.lower() if val else None
466
473
467
474
468 class UserLog(Base, BaseModel):
475 class UserLog(Base, BaseModel):
469 __tablename__ = 'user_logs'
476 __tablename__ = 'user_logs'
470 __table_args__ = (
477 __table_args__ = (
471 {'extend_existing': True, 'mysql_engine': 'InnoDB',
478 {'extend_existing': True, 'mysql_engine': 'InnoDB',
472 'mysql_charset': 'utf8'},
479 'mysql_charset': 'utf8'},
473 )
480 )
474 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
481 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
475 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
482 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
476 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
483 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
477 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
484 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
478 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
485 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
479 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
486 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
480 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
487 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
481
488
482 @property
489 @property
483 def action_as_day(self):
490 def action_as_day(self):
484 return datetime.date(*self.action_date.timetuple()[:3])
491 return datetime.date(*self.action_date.timetuple()[:3])
485
492
486 user = relationship('User')
493 user = relationship('User')
487 repository = relationship('Repository', cascade='')
494 repository = relationship('Repository', cascade='')
488
495
489
496
490 class UsersGroup(Base, BaseModel):
497 class UsersGroup(Base, BaseModel):
491 __tablename__ = 'users_groups'
498 __tablename__ = 'users_groups'
492 __table_args__ = (
499 __table_args__ = (
493 {'extend_existing': True, 'mysql_engine': 'InnoDB',
500 {'extend_existing': True, 'mysql_engine': 'InnoDB',
494 'mysql_charset': 'utf8'},
501 'mysql_charset': 'utf8'},
495 )
502 )
496
503
497 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
504 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
498 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
505 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
499 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
506 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
500
507
501 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
508 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
502 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
509 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
503 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
510 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
504
511
505 def __unicode__(self):
512 def __unicode__(self):
506 return u'<userGroup(%s)>' % (self.users_group_name)
513 return u'<userGroup(%s)>' % (self.users_group_name)
507
514
508 @classmethod
515 @classmethod
509 def get_by_group_name(cls, group_name, cache=False,
516 def get_by_group_name(cls, group_name, cache=False,
510 case_insensitive=False):
517 case_insensitive=False):
511 if case_insensitive:
518 if case_insensitive:
512 q = cls.query().filter(cls.users_group_name.ilike(group_name))
519 q = cls.query().filter(cls.users_group_name.ilike(group_name))
513 else:
520 else:
514 q = cls.query().filter(cls.users_group_name == group_name)
521 q = cls.query().filter(cls.users_group_name == group_name)
515 if cache:
522 if cache:
516 q = q.options(FromCache(
523 q = q.options(FromCache(
517 "sql_cache_short",
524 "sql_cache_short",
518 "get_user_%s" % _hash_key(group_name)
525 "get_user_%s" % _hash_key(group_name)
519 )
526 )
520 )
527 )
521 return q.scalar()
528 return q.scalar()
522
529
523 @classmethod
530 @classmethod
524 def get(cls, users_group_id, cache=False):
531 def get(cls, users_group_id, cache=False):
525 users_group = cls.query()
532 users_group = cls.query()
526 if cache:
533 if cache:
527 users_group = users_group.options(FromCache("sql_cache_short",
534 users_group = users_group.options(FromCache("sql_cache_short",
528 "get_users_group_%s" % users_group_id))
535 "get_users_group_%s" % users_group_id))
529 return users_group.get(users_group_id)
536 return users_group.get(users_group_id)
530
537
531 def get_api_data(self):
538 def get_api_data(self):
532 users_group = self
539 users_group = self
533
540
534 data = dict(
541 data = dict(
535 users_group_id=users_group.users_group_id,
542 users_group_id=users_group.users_group_id,
536 group_name=users_group.users_group_name,
543 group_name=users_group.users_group_name,
537 active=users_group.users_group_active,
544 active=users_group.users_group_active,
538 )
545 )
539
546
540 return data
547 return data
541
548
542
549
543 class UsersGroupMember(Base, BaseModel):
550 class UsersGroupMember(Base, BaseModel):
544 __tablename__ = 'users_groups_members'
551 __tablename__ = 'users_groups_members'
545 __table_args__ = (
552 __table_args__ = (
546 {'extend_existing': True, 'mysql_engine': 'InnoDB',
553 {'extend_existing': True, 'mysql_engine': 'InnoDB',
547 'mysql_charset': 'utf8'},
554 'mysql_charset': 'utf8'},
548 )
555 )
549
556
550 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
557 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
551 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
558 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
552 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
559 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
553
560
554 user = relationship('User', lazy='joined')
561 user = relationship('User', lazy='joined')
555 users_group = relationship('UsersGroup')
562 users_group = relationship('UsersGroup')
556
563
557 def __init__(self, gr_id='', u_id=''):
564 def __init__(self, gr_id='', u_id=''):
558 self.users_group_id = gr_id
565 self.users_group_id = gr_id
559 self.user_id = u_id
566 self.user_id = u_id
560
567
561
568
562 class Repository(Base, BaseModel):
569 class Repository(Base, BaseModel):
563 __tablename__ = 'repositories'
570 __tablename__ = 'repositories'
564 __table_args__ = (
571 __table_args__ = (
565 UniqueConstraint('repo_name'),
572 UniqueConstraint('repo_name'),
566 {'extend_existing': True, 'mysql_engine': 'InnoDB',
573 {'extend_existing': True, 'mysql_engine': 'InnoDB',
567 'mysql_charset': 'utf8'},
574 'mysql_charset': 'utf8'},
568 )
575 )
569
576
570 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
577 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
571 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
578 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
572 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
579 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
573 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
580 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
574 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
581 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
575 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
582 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
576 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
583 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
577 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
584 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
578 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
585 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
579 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
586 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
580 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
587 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
581
588
582 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
589 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
583 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
590 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
584
591
585 user = relationship('User')
592 user = relationship('User')
586 fork = relationship('Repository', remote_side=repo_id)
593 fork = relationship('Repository', remote_side=repo_id)
587 group = relationship('RepoGroup')
594 group = relationship('RepoGroup')
588 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
595 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
589 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
596 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
590 stats = relationship('Statistics', cascade='all', uselist=False)
597 stats = relationship('Statistics', cascade='all', uselist=False)
591
598
592 followers = relationship('UserFollowing',
599 followers = relationship('UserFollowing',
593 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
600 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
594 cascade='all')
601 cascade='all')
595
602
596 logs = relationship('UserLog')
603 logs = relationship('UserLog')
597 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
604 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
598
605
599 pull_requests_org = relationship('PullRequest',
606 pull_requests_org = relationship('PullRequest',
600 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
607 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
601 cascade="all, delete, delete-orphan")
608 cascade="all, delete, delete-orphan")
602
609
603 pull_requests_other = relationship('PullRequest',
610 pull_requests_other = relationship('PullRequest',
604 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
611 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
605 cascade="all, delete, delete-orphan")
612 cascade="all, delete, delete-orphan")
606
613
607 def __unicode__(self):
614 def __unicode__(self):
608 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
615 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
609 self.repo_name)
616 self.repo_name)
610
617
611 @classmethod
618 @classmethod
612 def url_sep(cls):
619 def url_sep(cls):
613 return URL_SEP
620 return URL_SEP
614
621
615 @classmethod
622 @classmethod
616 def get_by_repo_name(cls, repo_name):
623 def get_by_repo_name(cls, repo_name):
617 q = Session().query(cls).filter(cls.repo_name == repo_name)
624 q = Session().query(cls).filter(cls.repo_name == repo_name)
618 q = q.options(joinedload(Repository.fork))\
625 q = q.options(joinedload(Repository.fork))\
619 .options(joinedload(Repository.user))\
626 .options(joinedload(Repository.user))\
620 .options(joinedload(Repository.group))
627 .options(joinedload(Repository.group))
621 return q.scalar()
628 return q.scalar()
622
629
623 @classmethod
630 @classmethod
624 def get_by_full_path(cls, repo_full_path):
631 def get_by_full_path(cls, repo_full_path):
625 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
632 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
626 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
633 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
627
634
628 @classmethod
635 @classmethod
629 def get_repo_forks(cls, repo_id):
636 def get_repo_forks(cls, repo_id):
630 return cls.query().filter(Repository.fork_id == repo_id)
637 return cls.query().filter(Repository.fork_id == repo_id)
631
638
632 @classmethod
639 @classmethod
633 def base_path(cls):
640 def base_path(cls):
634 """
641 """
635 Returns base path when all repos are stored
642 Returns base path when all repos are stored
636
643
637 :param cls:
644 :param cls:
638 """
645 """
639 q = Session().query(RhodeCodeUi)\
646 q = Session().query(RhodeCodeUi)\
640 .filter(RhodeCodeUi.ui_key == cls.url_sep())
647 .filter(RhodeCodeUi.ui_key == cls.url_sep())
641 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
648 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
642 return q.one().ui_value
649 return q.one().ui_value
643
650
644 @property
651 @property
645 def forks(self):
652 def forks(self):
646 """
653 """
647 Return forks of this repo
654 Return forks of this repo
648 """
655 """
649 return Repository.get_repo_forks(self.repo_id)
656 return Repository.get_repo_forks(self.repo_id)
650
657
651 @property
658 @property
652 def parent(self):
659 def parent(self):
653 """
660 """
654 Returns fork parent
661 Returns fork parent
655 """
662 """
656 return self.fork
663 return self.fork
657
664
658 @property
665 @property
659 def just_name(self):
666 def just_name(self):
660 return self.repo_name.split(Repository.url_sep())[-1]
667 return self.repo_name.split(Repository.url_sep())[-1]
661
668
662 @property
669 @property
663 def groups_with_parents(self):
670 def groups_with_parents(self):
664 groups = []
671 groups = []
665 if self.group is None:
672 if self.group is None:
666 return groups
673 return groups
667
674
668 cur_gr = self.group
675 cur_gr = self.group
669 groups.insert(0, cur_gr)
676 groups.insert(0, cur_gr)
670 while 1:
677 while 1:
671 gr = getattr(cur_gr, 'parent_group', None)
678 gr = getattr(cur_gr, 'parent_group', None)
672 cur_gr = cur_gr.parent_group
679 cur_gr = cur_gr.parent_group
673 if gr is None:
680 if gr is None:
674 break
681 break
675 groups.insert(0, gr)
682 groups.insert(0, gr)
676
683
677 return groups
684 return groups
678
685
679 @property
686 @property
680 def groups_and_repo(self):
687 def groups_and_repo(self):
681 return self.groups_with_parents, self.just_name
688 return self.groups_with_parents, self.just_name
682
689
683 @LazyProperty
690 @LazyProperty
684 def repo_path(self):
691 def repo_path(self):
685 """
692 """
686 Returns base full path for that repository means where it actually
693 Returns base full path for that repository means where it actually
687 exists on a filesystem
694 exists on a filesystem
688 """
695 """
689 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
696 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
690 Repository.url_sep())
697 Repository.url_sep())
691 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
698 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
692 return q.one().ui_value
699 return q.one().ui_value
693
700
694 @property
701 @property
695 def repo_full_path(self):
702 def repo_full_path(self):
696 p = [self.repo_path]
703 p = [self.repo_path]
697 # we need to split the name by / since this is how we store the
704 # we need to split the name by / since this is how we store the
698 # names in the database, but that eventually needs to be converted
705 # names in the database, but that eventually needs to be converted
699 # into a valid system path
706 # into a valid system path
700 p += self.repo_name.split(Repository.url_sep())
707 p += self.repo_name.split(Repository.url_sep())
701 return os.path.join(*p)
708 return os.path.join(*p)
702
709
703 def get_new_name(self, repo_name):
710 def get_new_name(self, repo_name):
704 """
711 """
705 returns new full repository name based on assigned group and new new
712 returns new full repository name based on assigned group and new new
706
713
707 :param group_name:
714 :param group_name:
708 """
715 """
709 path_prefix = self.group.full_path_splitted if self.group else []
716 path_prefix = self.group.full_path_splitted if self.group else []
710 return Repository.url_sep().join(path_prefix + [repo_name])
717 return Repository.url_sep().join(path_prefix + [repo_name])
711
718
712 @property
719 @property
713 def _ui(self):
720 def _ui(self):
714 """
721 """
715 Creates an db based ui object for this repository
722 Creates an db based ui object for this repository
716 """
723 """
717 from mercurial import ui
724 from mercurial import ui
718 from mercurial import config
725 from mercurial import config
719 baseui = ui.ui()
726 baseui = ui.ui()
720
727
721 #clean the baseui object
728 #clean the baseui object
722 baseui._ocfg = config.config()
729 baseui._ocfg = config.config()
723 baseui._ucfg = config.config()
730 baseui._ucfg = config.config()
724 baseui._tcfg = config.config()
731 baseui._tcfg = config.config()
725
732
726 ret = RhodeCodeUi.query()\
733 ret = RhodeCodeUi.query()\
727 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
734 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
728
735
729 hg_ui = ret
736 hg_ui = ret
730 for ui_ in hg_ui:
737 for ui_ in hg_ui:
731 if ui_.ui_active and ui_.ui_key != 'push_ssl':
738 if ui_.ui_active and ui_.ui_key != 'push_ssl':
732 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
739 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
733 ui_.ui_key, ui_.ui_value)
740 ui_.ui_key, ui_.ui_value)
734 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
741 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
735
742
736 return baseui
743 return baseui
737
744
738 @classmethod
745 @classmethod
739 def inject_ui(cls, repo, extras={}):
746 def inject_ui(cls, repo, extras={}):
740 from rhodecode.lib.vcs.backends.hg import MercurialRepository
747 from rhodecode.lib.vcs.backends.hg import MercurialRepository
741 from rhodecode.lib.vcs.backends.git import GitRepository
748 from rhodecode.lib.vcs.backends.git import GitRepository
742 required = (MercurialRepository, GitRepository)
749 required = (MercurialRepository, GitRepository)
743 if not isinstance(repo, required):
750 if not isinstance(repo, required):
744 raise Exception('repo must be instance of %s' % required)
751 raise Exception('repo must be instance of %s' % required)
745
752
746 # inject ui extra param to log this action via push logger
753 # inject ui extra param to log this action via push logger
747 for k, v in extras.items():
754 for k, v in extras.items():
748 repo._repo.ui.setconfig('rhodecode_extras', k, v)
755 repo._repo.ui.setconfig('rhodecode_extras', k, v)
749
756
750 @classmethod
757 @classmethod
751 def is_valid(cls, repo_name):
758 def is_valid(cls, repo_name):
752 """
759 """
753 returns True if given repo name is a valid filesystem repository
760 returns True if given repo name is a valid filesystem repository
754
761
755 :param cls:
762 :param cls:
756 :param repo_name:
763 :param repo_name:
757 """
764 """
758 from rhodecode.lib.utils import is_valid_repo
765 from rhodecode.lib.utils import is_valid_repo
759
766
760 return is_valid_repo(repo_name, cls.base_path())
767 return is_valid_repo(repo_name, cls.base_path())
761
768
762 def get_api_data(self):
769 def get_api_data(self):
763 """
770 """
764 Common function for generating repo api data
771 Common function for generating repo api data
765
772
766 """
773 """
767 repo = self
774 repo = self
768 data = dict(
775 data = dict(
769 repo_id=repo.repo_id,
776 repo_id=repo.repo_id,
770 repo_name=repo.repo_name,
777 repo_name=repo.repo_name,
771 repo_type=repo.repo_type,
778 repo_type=repo.repo_type,
772 clone_uri=repo.clone_uri,
779 clone_uri=repo.clone_uri,
773 private=repo.private,
780 private=repo.private,
774 created_on=repo.created_on,
781 created_on=repo.created_on,
775 description=repo.description,
782 description=repo.description,
776 landing_rev=repo.landing_rev,
783 landing_rev=repo.landing_rev,
777 owner=repo.user.username,
784 owner=repo.user.username,
778 fork_of=repo.fork.repo_name if repo.fork else None
785 fork_of=repo.fork.repo_name if repo.fork else None
779 )
786 )
780
787
781 return data
788 return data
782
789
783 #==========================================================================
790 #==========================================================================
784 # SCM PROPERTIES
791 # SCM PROPERTIES
785 #==========================================================================
792 #==========================================================================
786
793
787 def get_changeset(self, rev=None):
794 def get_changeset(self, rev=None):
788 return get_changeset_safe(self.scm_instance, rev)
795 return get_changeset_safe(self.scm_instance, rev)
789
796
790 def get_landing_changeset(self):
797 def get_landing_changeset(self):
791 """
798 """
792 Returns landing changeset, or if that doesn't exist returns the tip
799 Returns landing changeset, or if that doesn't exist returns the tip
793 """
800 """
794 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
801 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
795 return cs
802 return cs
796
803
797 @property
804 @property
798 def tip(self):
805 def tip(self):
799 return self.get_changeset('tip')
806 return self.get_changeset('tip')
800
807
801 @property
808 @property
802 def author(self):
809 def author(self):
803 return self.tip.author
810 return self.tip.author
804
811
805 @property
812 @property
806 def last_change(self):
813 def last_change(self):
807 return self.scm_instance.last_change
814 return self.scm_instance.last_change
808
815
809 def get_comments(self, revisions=None):
816 def get_comments(self, revisions=None):
810 """
817 """
811 Returns comments for this repository grouped by revisions
818 Returns comments for this repository grouped by revisions
812
819
813 :param revisions: filter query by revisions only
820 :param revisions: filter query by revisions only
814 """
821 """
815 cmts = ChangesetComment.query()\
822 cmts = ChangesetComment.query()\
816 .filter(ChangesetComment.repo == self)
823 .filter(ChangesetComment.repo == self)
817 if revisions:
824 if revisions:
818 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
825 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
819 grouped = defaultdict(list)
826 grouped = defaultdict(list)
820 for cmt in cmts.all():
827 for cmt in cmts.all():
821 grouped[cmt.revision].append(cmt)
828 grouped[cmt.revision].append(cmt)
822 return grouped
829 return grouped
823
830
824 def statuses(self, revisions=None):
831 def statuses(self, revisions=None):
825 """
832 """
826 Returns statuses for this repository
833 Returns statuses for this repository
827
834
828 :param revisions: list of revisions to get statuses for
835 :param revisions: list of revisions to get statuses for
829 :type revisions: list
836 :type revisions: list
830 """
837 """
831
838
832 statuses = ChangesetStatus.query()\
839 statuses = ChangesetStatus.query()\
833 .filter(ChangesetStatus.repo == self)\
840 .filter(ChangesetStatus.repo == self)\
834 .filter(ChangesetStatus.version == 0)
841 .filter(ChangesetStatus.version == 0)
835 if revisions:
842 if revisions:
836 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
843 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
837 grouped = {}
844 grouped = {}
838
845
839 #maybe we have open new pullrequest without a status ?
846 #maybe we have open new pullrequest without a status ?
840 stat = ChangesetStatus.STATUS_UNDER_REVIEW
847 stat = ChangesetStatus.STATUS_UNDER_REVIEW
841 status_lbl = ChangesetStatus.get_status_lbl(stat)
848 status_lbl = ChangesetStatus.get_status_lbl(stat)
842 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
849 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
843 for rev in pr.revisions:
850 for rev in pr.revisions:
844 pr_id = pr.pull_request_id
851 pr_id = pr.pull_request_id
845 pr_repo = pr.other_repo.repo_name
852 pr_repo = pr.other_repo.repo_name
846 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
853 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
847
854
848 for stat in statuses.all():
855 for stat in statuses.all():
849 pr_id = pr_repo = None
856 pr_id = pr_repo = None
850 if stat.pull_request:
857 if stat.pull_request:
851 pr_id = stat.pull_request.pull_request_id
858 pr_id = stat.pull_request.pull_request_id
852 pr_repo = stat.pull_request.other_repo.repo_name
859 pr_repo = stat.pull_request.other_repo.repo_name
853 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
860 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
854 pr_id, pr_repo]
861 pr_id, pr_repo]
855 return grouped
862 return grouped
856
863
857 #==========================================================================
864 #==========================================================================
858 # SCM CACHE INSTANCE
865 # SCM CACHE INSTANCE
859 #==========================================================================
866 #==========================================================================
860
867
861 @property
868 @property
862 def invalidate(self):
869 def invalidate(self):
863 return CacheInvalidation.invalidate(self.repo_name)
870 return CacheInvalidation.invalidate(self.repo_name)
864
871
865 def set_invalidate(self):
872 def set_invalidate(self):
866 """
873 """
867 set a cache for invalidation for this instance
874 set a cache for invalidation for this instance
868 """
875 """
869 CacheInvalidation.set_invalidate(self.repo_name)
876 CacheInvalidation.set_invalidate(self.repo_name)
870
877
871 @LazyProperty
878 @LazyProperty
872 def scm_instance(self):
879 def scm_instance(self):
873 return self.__get_instance()
880 return self.__get_instance()
874
881
875 def scm_instance_cached(self, cache_map=None):
882 def scm_instance_cached(self, cache_map=None):
876 @cache_region('long_term')
883 @cache_region('long_term')
877 def _c(repo_name):
884 def _c(repo_name):
878 return self.__get_instance()
885 return self.__get_instance()
879 rn = self.repo_name
886 rn = self.repo_name
880 log.debug('Getting cached instance of repo')
887 log.debug('Getting cached instance of repo')
881
888
882 if cache_map:
889 if cache_map:
883 # get using prefilled cache_map
890 # get using prefilled cache_map
884 invalidate_repo = cache_map[self.repo_name]
891 invalidate_repo = cache_map[self.repo_name]
885 if invalidate_repo:
892 if invalidate_repo:
886 invalidate_repo = (None if invalidate_repo.cache_active
893 invalidate_repo = (None if invalidate_repo.cache_active
887 else invalidate_repo)
894 else invalidate_repo)
888 else:
895 else:
889 # get from invalidate
896 # get from invalidate
890 invalidate_repo = self.invalidate
897 invalidate_repo = self.invalidate
891
898
892 if invalidate_repo is not None:
899 if invalidate_repo is not None:
893 region_invalidate(_c, None, rn)
900 region_invalidate(_c, None, rn)
894 # update our cache
901 # update our cache
895 CacheInvalidation.set_valid(invalidate_repo.cache_key)
902 CacheInvalidation.set_valid(invalidate_repo.cache_key)
896 return _c(rn)
903 return _c(rn)
897
904
898 def __get_instance(self):
905 def __get_instance(self):
899 repo_full_path = self.repo_full_path
906 repo_full_path = self.repo_full_path
900 try:
907 try:
901 alias = get_scm(repo_full_path)[0]
908 alias = get_scm(repo_full_path)[0]
902 log.debug('Creating instance of %s repository' % alias)
909 log.debug('Creating instance of %s repository' % alias)
903 backend = get_backend(alias)
910 backend = get_backend(alias)
904 except VCSError:
911 except VCSError:
905 log.error(traceback.format_exc())
912 log.error(traceback.format_exc())
906 log.error('Perhaps this repository is in db and not in '
913 log.error('Perhaps this repository is in db and not in '
907 'filesystem run rescan repositories with '
914 'filesystem run rescan repositories with '
908 '"destroy old data " option from admin panel')
915 '"destroy old data " option from admin panel')
909 return
916 return
910
917
911 if alias == 'hg':
918 if alias == 'hg':
912
919
913 repo = backend(safe_str(repo_full_path), create=False,
920 repo = backend(safe_str(repo_full_path), create=False,
914 baseui=self._ui)
921 baseui=self._ui)
915 # skip hidden web repository
922 # skip hidden web repository
916 if repo._get_hidden():
923 if repo._get_hidden():
917 return
924 return
918 else:
925 else:
919 repo = backend(repo_full_path, create=False)
926 repo = backend(repo_full_path, create=False)
920
927
921 return repo
928 return repo
922
929
923
930
924 class RepoGroup(Base, BaseModel):
931 class RepoGroup(Base, BaseModel):
925 __tablename__ = 'groups'
932 __tablename__ = 'groups'
926 __table_args__ = (
933 __table_args__ = (
927 UniqueConstraint('group_name', 'group_parent_id'),
934 UniqueConstraint('group_name', 'group_parent_id'),
928 CheckConstraint('group_id != group_parent_id'),
935 CheckConstraint('group_id != group_parent_id'),
929 {'extend_existing': True, 'mysql_engine': 'InnoDB',
936 {'extend_existing': True, 'mysql_engine': 'InnoDB',
930 'mysql_charset': 'utf8'},
937 'mysql_charset': 'utf8'},
931 )
938 )
932 __mapper_args__ = {'order_by': 'group_name'}
939 __mapper_args__ = {'order_by': 'group_name'}
933
940
934 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
941 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
935 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
942 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
936 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
943 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
937 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
944 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
938
945
939 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
946 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
940 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
947 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
941
948
942 parent_group = relationship('RepoGroup', remote_side=group_id)
949 parent_group = relationship('RepoGroup', remote_side=group_id)
943
950
944 def __init__(self, group_name='', parent_group=None):
951 def __init__(self, group_name='', parent_group=None):
945 self.group_name = group_name
952 self.group_name = group_name
946 self.parent_group = parent_group
953 self.parent_group = parent_group
947
954
948 def __unicode__(self):
955 def __unicode__(self):
949 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
956 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
950 self.group_name)
957 self.group_name)
951
958
952 @classmethod
959 @classmethod
953 def groups_choices(cls):
960 def groups_choices(cls):
954 from webhelpers.html import literal as _literal
961 from webhelpers.html import literal as _literal
955 repo_groups = [('', '')]
962 repo_groups = [('', '')]
956 sep = ' &raquo; '
963 sep = ' &raquo; '
957 _name = lambda k: _literal(sep.join(k))
964 _name = lambda k: _literal(sep.join(k))
958
965
959 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
966 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
960 for x in cls.query().all()])
967 for x in cls.query().all()])
961
968
962 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
969 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
963 return repo_groups
970 return repo_groups
964
971
965 @classmethod
972 @classmethod
966 def url_sep(cls):
973 def url_sep(cls):
967 return URL_SEP
974 return URL_SEP
968
975
969 @classmethod
976 @classmethod
970 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
977 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
971 if case_insensitive:
978 if case_insensitive:
972 gr = cls.query()\
979 gr = cls.query()\
973 .filter(cls.group_name.ilike(group_name))
980 .filter(cls.group_name.ilike(group_name))
974 else:
981 else:
975 gr = cls.query()\
982 gr = cls.query()\
976 .filter(cls.group_name == group_name)
983 .filter(cls.group_name == group_name)
977 if cache:
984 if cache:
978 gr = gr.options(FromCache(
985 gr = gr.options(FromCache(
979 "sql_cache_short",
986 "sql_cache_short",
980 "get_group_%s" % _hash_key(group_name)
987 "get_group_%s" % _hash_key(group_name)
981 )
988 )
982 )
989 )
983 return gr.scalar()
990 return gr.scalar()
984
991
985 @property
992 @property
986 def parents(self):
993 def parents(self):
987 parents_recursion_limit = 5
994 parents_recursion_limit = 5
988 groups = []
995 groups = []
989 if self.parent_group is None:
996 if self.parent_group is None:
990 return groups
997 return groups
991 cur_gr = self.parent_group
998 cur_gr = self.parent_group
992 groups.insert(0, cur_gr)
999 groups.insert(0, cur_gr)
993 cnt = 0
1000 cnt = 0
994 while 1:
1001 while 1:
995 cnt += 1
1002 cnt += 1
996 gr = getattr(cur_gr, 'parent_group', None)
1003 gr = getattr(cur_gr, 'parent_group', None)
997 cur_gr = cur_gr.parent_group
1004 cur_gr = cur_gr.parent_group
998 if gr is None:
1005 if gr is None:
999 break
1006 break
1000 if cnt == parents_recursion_limit:
1007 if cnt == parents_recursion_limit:
1001 # this will prevent accidental infinit loops
1008 # this will prevent accidental infinit loops
1002 log.error('group nested more than %s' %
1009 log.error('group nested more than %s' %
1003 parents_recursion_limit)
1010 parents_recursion_limit)
1004 break
1011 break
1005
1012
1006 groups.insert(0, gr)
1013 groups.insert(0, gr)
1007 return groups
1014 return groups
1008
1015
1009 @property
1016 @property
1010 def children(self):
1017 def children(self):
1011 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1018 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1012
1019
1013 @property
1020 @property
1014 def name(self):
1021 def name(self):
1015 return self.group_name.split(RepoGroup.url_sep())[-1]
1022 return self.group_name.split(RepoGroup.url_sep())[-1]
1016
1023
1017 @property
1024 @property
1018 def full_path(self):
1025 def full_path(self):
1019 return self.group_name
1026 return self.group_name
1020
1027
1021 @property
1028 @property
1022 def full_path_splitted(self):
1029 def full_path_splitted(self):
1023 return self.group_name.split(RepoGroup.url_sep())
1030 return self.group_name.split(RepoGroup.url_sep())
1024
1031
1025 @property
1032 @property
1026 def repositories(self):
1033 def repositories(self):
1027 return Repository.query()\
1034 return Repository.query()\
1028 .filter(Repository.group == self)\
1035 .filter(Repository.group == self)\
1029 .order_by(Repository.repo_name)
1036 .order_by(Repository.repo_name)
1030
1037
1031 @property
1038 @property
1032 def repositories_recursive_count(self):
1039 def repositories_recursive_count(self):
1033 cnt = self.repositories.count()
1040 cnt = self.repositories.count()
1034
1041
1035 def children_count(group):
1042 def children_count(group):
1036 cnt = 0
1043 cnt = 0
1037 for child in group.children:
1044 for child in group.children:
1038 cnt += child.repositories.count()
1045 cnt += child.repositories.count()
1039 cnt += children_count(child)
1046 cnt += children_count(child)
1040 return cnt
1047 return cnt
1041
1048
1042 return cnt + children_count(self)
1049 return cnt + children_count(self)
1043
1050
1044 def get_new_name(self, group_name):
1051 def get_new_name(self, group_name):
1045 """
1052 """
1046 returns new full group name based on parent and new name
1053 returns new full group name based on parent and new name
1047
1054
1048 :param group_name:
1055 :param group_name:
1049 """
1056 """
1050 path_prefix = (self.parent_group.full_path_splitted if
1057 path_prefix = (self.parent_group.full_path_splitted if
1051 self.parent_group else [])
1058 self.parent_group else [])
1052 return RepoGroup.url_sep().join(path_prefix + [group_name])
1059 return RepoGroup.url_sep().join(path_prefix + [group_name])
1053
1060
1054
1061
1055 class Permission(Base, BaseModel):
1062 class Permission(Base, BaseModel):
1056 __tablename__ = 'permissions'
1063 __tablename__ = 'permissions'
1057 __table_args__ = (
1064 __table_args__ = (
1058 Index('p_perm_name_idx', 'permission_name'),
1065 Index('p_perm_name_idx', 'permission_name'),
1059 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1066 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1060 'mysql_charset': 'utf8'},
1067 'mysql_charset': 'utf8'},
1061 )
1068 )
1062 PERMS = [
1069 PERMS = [
1063 ('repository.none', _('Repository no access')),
1070 ('repository.none', _('Repository no access')),
1064 ('repository.read', _('Repository read access')),
1071 ('repository.read', _('Repository read access')),
1065 ('repository.write', _('Repository write access')),
1072 ('repository.write', _('Repository write access')),
1066 ('repository.admin', _('Repository admin access')),
1073 ('repository.admin', _('Repository admin access')),
1067
1074
1068 ('group.none', _('Repositories Group no access')),
1075 ('group.none', _('Repositories Group no access')),
1069 ('group.read', _('Repositories Group read access')),
1076 ('group.read', _('Repositories Group read access')),
1070 ('group.write', _('Repositories Group write access')),
1077 ('group.write', _('Repositories Group write access')),
1071 ('group.admin', _('Repositories Group admin access')),
1078 ('group.admin', _('Repositories Group admin access')),
1072
1079
1073 ('hg.admin', _('RhodeCode Administrator')),
1080 ('hg.admin', _('RhodeCode Administrator')),
1074 ('hg.create.none', _('Repository creation disabled')),
1081 ('hg.create.none', _('Repository creation disabled')),
1075 ('hg.create.repository', _('Repository creation enabled')),
1082 ('hg.create.repository', _('Repository creation enabled')),
1076 ('hg.register.none', _('Register disabled')),
1083 ('hg.register.none', _('Register disabled')),
1077 ('hg.register.manual_activate', _('Register new user with RhodeCode '
1084 ('hg.register.manual_activate', _('Register new user with RhodeCode '
1078 'with manual activation')),
1085 'with manual activation')),
1079
1086
1080 ('hg.register.auto_activate', _('Register new user with RhodeCode '
1087 ('hg.register.auto_activate', _('Register new user with RhodeCode '
1081 'with auto activation')),
1088 'with auto activation')),
1082 ]
1089 ]
1083
1090
1084 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1091 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1085 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1092 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1086 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1093 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1087
1094
1088 def __unicode__(self):
1095 def __unicode__(self):
1089 return u"<%s('%s:%s')>" % (
1096 return u"<%s('%s:%s')>" % (
1090 self.__class__.__name__, self.permission_id, self.permission_name
1097 self.__class__.__name__, self.permission_id, self.permission_name
1091 )
1098 )
1092
1099
1093 @classmethod
1100 @classmethod
1094 def get_by_key(cls, key):
1101 def get_by_key(cls, key):
1095 return cls.query().filter(cls.permission_name == key).scalar()
1102 return cls.query().filter(cls.permission_name == key).scalar()
1096
1103
1097 @classmethod
1104 @classmethod
1098 def get_default_perms(cls, default_user_id):
1105 def get_default_perms(cls, default_user_id):
1099 q = Session().query(UserRepoToPerm, Repository, cls)\
1106 q = Session().query(UserRepoToPerm, Repository, cls)\
1100 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1107 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1101 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1108 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1102 .filter(UserRepoToPerm.user_id == default_user_id)
1109 .filter(UserRepoToPerm.user_id == default_user_id)
1103
1110
1104 return q.all()
1111 return q.all()
1105
1112
1106 @classmethod
1113 @classmethod
1107 def get_default_group_perms(cls, default_user_id):
1114 def get_default_group_perms(cls, default_user_id):
1108 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1115 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1109 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1116 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1110 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1117 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1111 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1118 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1112
1119
1113 return q.all()
1120 return q.all()
1114
1121
1115
1122
1116 class UserRepoToPerm(Base, BaseModel):
1123 class UserRepoToPerm(Base, BaseModel):
1117 __tablename__ = 'repo_to_perm'
1124 __tablename__ = 'repo_to_perm'
1118 __table_args__ = (
1125 __table_args__ = (
1119 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1126 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1120 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1127 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1121 'mysql_charset': 'utf8'}
1128 'mysql_charset': 'utf8'}
1122 )
1129 )
1123 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1130 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1124 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1131 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1125 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1132 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1126 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1133 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1127
1134
1128 user = relationship('User')
1135 user = relationship('User')
1129 repository = relationship('Repository')
1136 repository = relationship('Repository')
1130 permission = relationship('Permission')
1137 permission = relationship('Permission')
1131
1138
1132 @classmethod
1139 @classmethod
1133 def create(cls, user, repository, permission):
1140 def create(cls, user, repository, permission):
1134 n = cls()
1141 n = cls()
1135 n.user = user
1142 n.user = user
1136 n.repository = repository
1143 n.repository = repository
1137 n.permission = permission
1144 n.permission = permission
1138 Session().add(n)
1145 Session().add(n)
1139 return n
1146 return n
1140
1147
1141 def __unicode__(self):
1148 def __unicode__(self):
1142 return u'<user:%s => %s >' % (self.user, self.repository)
1149 return u'<user:%s => %s >' % (self.user, self.repository)
1143
1150
1144
1151
1145 class UserToPerm(Base, BaseModel):
1152 class UserToPerm(Base, BaseModel):
1146 __tablename__ = 'user_to_perm'
1153 __tablename__ = 'user_to_perm'
1147 __table_args__ = (
1154 __table_args__ = (
1148 UniqueConstraint('user_id', 'permission_id'),
1155 UniqueConstraint('user_id', 'permission_id'),
1149 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1156 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1150 'mysql_charset': 'utf8'}
1157 'mysql_charset': 'utf8'}
1151 )
1158 )
1152 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1159 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1153 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1160 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1154 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1161 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1155
1162
1156 user = relationship('User')
1163 user = relationship('User')
1157 permission = relationship('Permission', lazy='joined')
1164 permission = relationship('Permission', lazy='joined')
1158
1165
1159
1166
1160 class UsersGroupRepoToPerm(Base, BaseModel):
1167 class UsersGroupRepoToPerm(Base, BaseModel):
1161 __tablename__ = 'users_group_repo_to_perm'
1168 __tablename__ = 'users_group_repo_to_perm'
1162 __table_args__ = (
1169 __table_args__ = (
1163 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1170 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1164 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1171 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1165 'mysql_charset': 'utf8'}
1172 'mysql_charset': 'utf8'}
1166 )
1173 )
1167 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1174 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1168 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1175 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1169 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1176 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1170 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1177 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1171
1178
1172 users_group = relationship('UsersGroup')
1179 users_group = relationship('UsersGroup')
1173 permission = relationship('Permission')
1180 permission = relationship('Permission')
1174 repository = relationship('Repository')
1181 repository = relationship('Repository')
1175
1182
1176 @classmethod
1183 @classmethod
1177 def create(cls, users_group, repository, permission):
1184 def create(cls, users_group, repository, permission):
1178 n = cls()
1185 n = cls()
1179 n.users_group = users_group
1186 n.users_group = users_group
1180 n.repository = repository
1187 n.repository = repository
1181 n.permission = permission
1188 n.permission = permission
1182 Session().add(n)
1189 Session().add(n)
1183 return n
1190 return n
1184
1191
1185 def __unicode__(self):
1192 def __unicode__(self):
1186 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1193 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1187
1194
1188
1195
1189 class UsersGroupToPerm(Base, BaseModel):
1196 class UsersGroupToPerm(Base, BaseModel):
1190 __tablename__ = 'users_group_to_perm'
1197 __tablename__ = 'users_group_to_perm'
1191 __table_args__ = (
1198 __table_args__ = (
1192 UniqueConstraint('users_group_id', 'permission_id',),
1199 UniqueConstraint('users_group_id', 'permission_id',),
1193 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1200 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1194 'mysql_charset': 'utf8'}
1201 'mysql_charset': 'utf8'}
1195 )
1202 )
1196 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1203 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1197 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1204 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1198 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1205 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1199
1206
1200 users_group = relationship('UsersGroup')
1207 users_group = relationship('UsersGroup')
1201 permission = relationship('Permission')
1208 permission = relationship('Permission')
1202
1209
1203
1210
1204 class UserRepoGroupToPerm(Base, BaseModel):
1211 class UserRepoGroupToPerm(Base, BaseModel):
1205 __tablename__ = 'user_repo_group_to_perm'
1212 __tablename__ = 'user_repo_group_to_perm'
1206 __table_args__ = (
1213 __table_args__ = (
1207 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1214 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1208 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1215 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1209 'mysql_charset': 'utf8'}
1216 'mysql_charset': 'utf8'}
1210 )
1217 )
1211
1218
1212 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1219 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1213 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1220 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1214 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1221 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1215 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1222 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1216
1223
1217 user = relationship('User')
1224 user = relationship('User')
1218 group = relationship('RepoGroup')
1225 group = relationship('RepoGroup')
1219 permission = relationship('Permission')
1226 permission = relationship('Permission')
1220
1227
1221
1228
1222 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1229 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1223 __tablename__ = 'users_group_repo_group_to_perm'
1230 __tablename__ = 'users_group_repo_group_to_perm'
1224 __table_args__ = (
1231 __table_args__ = (
1225 UniqueConstraint('users_group_id', 'group_id'),
1232 UniqueConstraint('users_group_id', 'group_id'),
1226 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1233 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1227 'mysql_charset': 'utf8'}
1234 'mysql_charset': 'utf8'}
1228 )
1235 )
1229
1236
1230 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1237 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1231 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1238 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1232 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1239 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1233 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1240 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1234
1241
1235 users_group = relationship('UsersGroup')
1242 users_group = relationship('UsersGroup')
1236 permission = relationship('Permission')
1243 permission = relationship('Permission')
1237 group = relationship('RepoGroup')
1244 group = relationship('RepoGroup')
1238
1245
1239
1246
1240 class Statistics(Base, BaseModel):
1247 class Statistics(Base, BaseModel):
1241 __tablename__ = 'statistics'
1248 __tablename__ = 'statistics'
1242 __table_args__ = (
1249 __table_args__ = (
1243 UniqueConstraint('repository_id'),
1250 UniqueConstraint('repository_id'),
1244 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1251 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1245 'mysql_charset': 'utf8'}
1252 'mysql_charset': 'utf8'}
1246 )
1253 )
1247 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1254 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1248 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1255 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1249 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1256 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1250 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1257 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1251 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1258 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1252 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1259 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1253
1260
1254 repository = relationship('Repository', single_parent=True)
1261 repository = relationship('Repository', single_parent=True)
1255
1262
1256
1263
1257 class UserFollowing(Base, BaseModel):
1264 class UserFollowing(Base, BaseModel):
1258 __tablename__ = 'user_followings'
1265 __tablename__ = 'user_followings'
1259 __table_args__ = (
1266 __table_args__ = (
1260 UniqueConstraint('user_id', 'follows_repository_id'),
1267 UniqueConstraint('user_id', 'follows_repository_id'),
1261 UniqueConstraint('user_id', 'follows_user_id'),
1268 UniqueConstraint('user_id', 'follows_user_id'),
1262 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1269 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1263 'mysql_charset': 'utf8'}
1270 'mysql_charset': 'utf8'}
1264 )
1271 )
1265
1272
1266 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1273 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1267 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1274 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1268 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1275 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1269 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1276 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1270 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1277 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1271
1278
1272 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1279 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1273
1280
1274 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1281 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1275 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1282 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1276
1283
1277 @classmethod
1284 @classmethod
1278 def get_repo_followers(cls, repo_id):
1285 def get_repo_followers(cls, repo_id):
1279 return cls.query().filter(cls.follows_repo_id == repo_id)
1286 return cls.query().filter(cls.follows_repo_id == repo_id)
1280
1287
1281
1288
1282 class CacheInvalidation(Base, BaseModel):
1289 class CacheInvalidation(Base, BaseModel):
1283 __tablename__ = 'cache_invalidation'
1290 __tablename__ = 'cache_invalidation'
1284 __table_args__ = (
1291 __table_args__ = (
1285 UniqueConstraint('cache_key'),
1292 UniqueConstraint('cache_key'),
1286 Index('key_idx', 'cache_key'),
1293 Index('key_idx', 'cache_key'),
1287 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1294 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1288 'mysql_charset': 'utf8'},
1295 'mysql_charset': 'utf8'},
1289 )
1296 )
1290 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1297 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1291 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1298 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1292 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1299 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1293 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1300 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1294
1301
1295 def __init__(self, cache_key, cache_args=''):
1302 def __init__(self, cache_key, cache_args=''):
1296 self.cache_key = cache_key
1303 self.cache_key = cache_key
1297 self.cache_args = cache_args
1304 self.cache_args = cache_args
1298 self.cache_active = False
1305 self.cache_active = False
1299
1306
1300 def __unicode__(self):
1307 def __unicode__(self):
1301 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1308 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1302 self.cache_id, self.cache_key)
1309 self.cache_id, self.cache_key)
1303
1310
1304 @classmethod
1311 @classmethod
1305 def clear_cache(cls):
1312 def clear_cache(cls):
1306 cls.query().delete()
1313 cls.query().delete()
1307
1314
1308 @classmethod
1315 @classmethod
1309 def _get_key(cls, key):
1316 def _get_key(cls, key):
1310 """
1317 """
1311 Wrapper for generating a key, together with a prefix
1318 Wrapper for generating a key, together with a prefix
1312
1319
1313 :param key:
1320 :param key:
1314 """
1321 """
1315 import rhodecode
1322 import rhodecode
1316 prefix = ''
1323 prefix = ''
1317 iid = rhodecode.CONFIG.get('instance_id')
1324 iid = rhodecode.CONFIG.get('instance_id')
1318 if iid:
1325 if iid:
1319 prefix = iid
1326 prefix = iid
1320 return "%s%s" % (prefix, key), prefix, key.rstrip('_README')
1327 return "%s%s" % (prefix, key), prefix, key.rstrip('_README')
1321
1328
1322 @classmethod
1329 @classmethod
1323 def get_by_key(cls, key):
1330 def get_by_key(cls, key):
1324 return cls.query().filter(cls.cache_key == key).scalar()
1331 return cls.query().filter(cls.cache_key == key).scalar()
1325
1332
1326 @classmethod
1333 @classmethod
1327 def _get_or_create_key(cls, key, prefix, org_key):
1334 def _get_or_create_key(cls, key, prefix, org_key):
1328 inv_obj = Session().query(cls).filter(cls.cache_key == key).scalar()
1335 inv_obj = Session().query(cls).filter(cls.cache_key == key).scalar()
1329 if not inv_obj:
1336 if not inv_obj:
1330 try:
1337 try:
1331 inv_obj = CacheInvalidation(key, org_key)
1338 inv_obj = CacheInvalidation(key, org_key)
1332 Session().add(inv_obj)
1339 Session().add(inv_obj)
1333 Session().commit()
1340 Session().commit()
1334 except Exception:
1341 except Exception:
1335 log.error(traceback.format_exc())
1342 log.error(traceback.format_exc())
1336 Session().rollback()
1343 Session().rollback()
1337 return inv_obj
1344 return inv_obj
1338
1345
1339 @classmethod
1346 @classmethod
1340 def invalidate(cls, key):
1347 def invalidate(cls, key):
1341 """
1348 """
1342 Returns Invalidation object if this given key should be invalidated
1349 Returns Invalidation object if this given key should be invalidated
1343 None otherwise. `cache_active = False` means that this cache
1350 None otherwise. `cache_active = False` means that this cache
1344 state is not valid and needs to be invalidated
1351 state is not valid and needs to be invalidated
1345
1352
1346 :param key:
1353 :param key:
1347 """
1354 """
1348
1355
1349 key, _prefix, _org_key = cls._get_key(key)
1356 key, _prefix, _org_key = cls._get_key(key)
1350 inv = cls._get_or_create_key(key, _prefix, _org_key)
1357 inv = cls._get_or_create_key(key, _prefix, _org_key)
1351
1358
1352 if inv and inv.cache_active is False:
1359 if inv and inv.cache_active is False:
1353 return inv
1360 return inv
1354
1361
1355 @classmethod
1362 @classmethod
1356 def set_invalidate(cls, key):
1363 def set_invalidate(cls, key):
1357 """
1364 """
1358 Mark this Cache key for invalidation
1365 Mark this Cache key for invalidation
1359
1366
1360 :param key:
1367 :param key:
1361 """
1368 """
1362
1369
1363 key, _prefix, _org_key = cls._get_key(key)
1370 key, _prefix, _org_key = cls._get_key(key)
1364 inv_objs = Session().query(cls).filter(cls.cache_args == _org_key).all()
1371 inv_objs = Session().query(cls).filter(cls.cache_args == _org_key).all()
1365 log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs),
1372 log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs),
1366 _org_key))
1373 _org_key))
1367 try:
1374 try:
1368 for inv_obj in inv_objs:
1375 for inv_obj in inv_objs:
1369 if inv_obj:
1376 if inv_obj:
1370 inv_obj.cache_active = False
1377 inv_obj.cache_active = False
1371
1378
1372 Session().add(inv_obj)
1379 Session().add(inv_obj)
1373 Session().commit()
1380 Session().commit()
1374 except Exception:
1381 except Exception:
1375 log.error(traceback.format_exc())
1382 log.error(traceback.format_exc())
1376 Session().rollback()
1383 Session().rollback()
1377
1384
1378 @classmethod
1385 @classmethod
1379 def set_valid(cls, key):
1386 def set_valid(cls, key):
1380 """
1387 """
1381 Mark this cache key as active and currently cached
1388 Mark this cache key as active and currently cached
1382
1389
1383 :param key:
1390 :param key:
1384 """
1391 """
1385 inv_obj = cls.get_by_key(key)
1392 inv_obj = cls.get_by_key(key)
1386 inv_obj.cache_active = True
1393 inv_obj.cache_active = True
1387 Session().add(inv_obj)
1394 Session().add(inv_obj)
1388 Session().commit()
1395 Session().commit()
1389
1396
1390 @classmethod
1397 @classmethod
1391 def get_cache_map(cls):
1398 def get_cache_map(cls):
1392
1399
1393 class cachemapdict(dict):
1400 class cachemapdict(dict):
1394
1401
1395 def __init__(self, *args, **kwargs):
1402 def __init__(self, *args, **kwargs):
1396 fixkey = kwargs.get('fixkey')
1403 fixkey = kwargs.get('fixkey')
1397 if fixkey:
1404 if fixkey:
1398 del kwargs['fixkey']
1405 del kwargs['fixkey']
1399 self.fixkey = fixkey
1406 self.fixkey = fixkey
1400 super(cachemapdict, self).__init__(*args, **kwargs)
1407 super(cachemapdict, self).__init__(*args, **kwargs)
1401
1408
1402 def __getattr__(self, name):
1409 def __getattr__(self, name):
1403 key = name
1410 key = name
1404 if self.fixkey:
1411 if self.fixkey:
1405 key, _prefix, _org_key = cls._get_key(key)
1412 key, _prefix, _org_key = cls._get_key(key)
1406 if key in self.__dict__:
1413 if key in self.__dict__:
1407 return self.__dict__[key]
1414 return self.__dict__[key]
1408 else:
1415 else:
1409 return self[key]
1416 return self[key]
1410
1417
1411 def __getitem__(self, key):
1418 def __getitem__(self, key):
1412 if self.fixkey:
1419 if self.fixkey:
1413 key, _prefix, _org_key = cls._get_key(key)
1420 key, _prefix, _org_key = cls._get_key(key)
1414 try:
1421 try:
1415 return super(cachemapdict, self).__getitem__(key)
1422 return super(cachemapdict, self).__getitem__(key)
1416 except KeyError:
1423 except KeyError:
1417 return
1424 return
1418
1425
1419 cache_map = cachemapdict(fixkey=True)
1426 cache_map = cachemapdict(fixkey=True)
1420 for obj in cls.query().all():
1427 for obj in cls.query().all():
1421 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1428 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1422 return cache_map
1429 return cache_map
1423
1430
1424
1431
1425 class ChangesetComment(Base, BaseModel):
1432 class ChangesetComment(Base, BaseModel):
1426 __tablename__ = 'changeset_comments'
1433 __tablename__ = 'changeset_comments'
1427 __table_args__ = (
1434 __table_args__ = (
1428 Index('cc_revision_idx', 'revision'),
1435 Index('cc_revision_idx', 'revision'),
1429 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1436 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1430 'mysql_charset': 'utf8'},
1437 'mysql_charset': 'utf8'},
1431 )
1438 )
1432 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1439 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1433 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1440 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1434 revision = Column('revision', String(40), nullable=True)
1441 revision = Column('revision', String(40), nullable=True)
1435 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1442 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1436 line_no = Column('line_no', Unicode(10), nullable=True)
1443 line_no = Column('line_no', Unicode(10), nullable=True)
1437 f_path = Column('f_path', Unicode(1000), nullable=True)
1444 f_path = Column('f_path', Unicode(1000), nullable=True)
1438 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1445 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1439 text = Column('text', Unicode(25000), nullable=False)
1446 text = Column('text', Unicode(25000), nullable=False)
1440 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1447 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1441 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1448 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1442
1449
1443 author = relationship('User', lazy='joined')
1450 author = relationship('User', lazy='joined')
1444 repo = relationship('Repository')
1451 repo = relationship('Repository')
1445 status_change = relationship('ChangesetStatus', uselist=False)
1452 status_change = relationship('ChangesetStatus', uselist=False)
1446 pull_request = relationship('PullRequest', lazy='joined')
1453 pull_request = relationship('PullRequest', lazy='joined')
1447
1454
1448 @classmethod
1455 @classmethod
1449 def get_users(cls, revision=None, pull_request_id=None):
1456 def get_users(cls, revision=None, pull_request_id=None):
1450 """
1457 """
1451 Returns user associated with this ChangesetComment. ie those
1458 Returns user associated with this ChangesetComment. ie those
1452 who actually commented
1459 who actually commented
1453
1460
1454 :param cls:
1461 :param cls:
1455 :param revision:
1462 :param revision:
1456 """
1463 """
1457 q = Session().query(User)\
1464 q = Session().query(User)\
1458 .join(ChangesetComment.author)
1465 .join(ChangesetComment.author)
1459 if revision:
1466 if revision:
1460 q = q.filter(cls.revision == revision)
1467 q = q.filter(cls.revision == revision)
1461 elif pull_request_id:
1468 elif pull_request_id:
1462 q = q.filter(cls.pull_request_id == pull_request_id)
1469 q = q.filter(cls.pull_request_id == pull_request_id)
1463 return q.all()
1470 return q.all()
1464
1471
1465
1472
1466 class ChangesetStatus(Base, BaseModel):
1473 class ChangesetStatus(Base, BaseModel):
1467 __tablename__ = 'changeset_statuses'
1474 __tablename__ = 'changeset_statuses'
1468 __table_args__ = (
1475 __table_args__ = (
1469 Index('cs_revision_idx', 'revision'),
1476 Index('cs_revision_idx', 'revision'),
1470 Index('cs_version_idx', 'version'),
1477 Index('cs_version_idx', 'version'),
1471 UniqueConstraint('repo_id', 'revision', 'version'),
1478 UniqueConstraint('repo_id', 'revision', 'version'),
1472 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1479 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1473 'mysql_charset': 'utf8'}
1480 'mysql_charset': 'utf8'}
1474 )
1481 )
1475 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1482 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1476 STATUS_APPROVED = 'approved'
1483 STATUS_APPROVED = 'approved'
1477 STATUS_REJECTED = 'rejected'
1484 STATUS_REJECTED = 'rejected'
1478 STATUS_UNDER_REVIEW = 'under_review'
1485 STATUS_UNDER_REVIEW = 'under_review'
1479
1486
1480 STATUSES = [
1487 STATUSES = [
1481 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1488 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1482 (STATUS_APPROVED, _("Approved")),
1489 (STATUS_APPROVED, _("Approved")),
1483 (STATUS_REJECTED, _("Rejected")),
1490 (STATUS_REJECTED, _("Rejected")),
1484 (STATUS_UNDER_REVIEW, _("Under Review")),
1491 (STATUS_UNDER_REVIEW, _("Under Review")),
1485 ]
1492 ]
1486
1493
1487 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1494 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1488 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1495 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1489 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1496 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1490 revision = Column('revision', String(40), nullable=False)
1497 revision = Column('revision', String(40), nullable=False)
1491 status = Column('status', String(128), nullable=False, default=DEFAULT)
1498 status = Column('status', String(128), nullable=False, default=DEFAULT)
1492 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1499 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1493 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1500 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1494 version = Column('version', Integer(), nullable=False, default=0)
1501 version = Column('version', Integer(), nullable=False, default=0)
1495 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1502 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1496
1503
1497 author = relationship('User', lazy='joined')
1504 author = relationship('User', lazy='joined')
1498 repo = relationship('Repository')
1505 repo = relationship('Repository')
1499 comment = relationship('ChangesetComment', lazy='joined')
1506 comment = relationship('ChangesetComment', lazy='joined')
1500 pull_request = relationship('PullRequest', lazy='joined')
1507 pull_request = relationship('PullRequest', lazy='joined')
1501
1508
1502 def __unicode__(self):
1509 def __unicode__(self):
1503 return u"<%s('%s:%s')>" % (
1510 return u"<%s('%s:%s')>" % (
1504 self.__class__.__name__,
1511 self.__class__.__name__,
1505 self.status, self.author
1512 self.status, self.author
1506 )
1513 )
1507
1514
1508 @classmethod
1515 @classmethod
1509 def get_status_lbl(cls, value):
1516 def get_status_lbl(cls, value):
1510 return dict(cls.STATUSES).get(value)
1517 return dict(cls.STATUSES).get(value)
1511
1518
1512 @property
1519 @property
1513 def status_lbl(self):
1520 def status_lbl(self):
1514 return ChangesetStatus.get_status_lbl(self.status)
1521 return ChangesetStatus.get_status_lbl(self.status)
1515
1522
1516
1523
1517 class PullRequest(Base, BaseModel):
1524 class PullRequest(Base, BaseModel):
1518 __tablename__ = 'pull_requests'
1525 __tablename__ = 'pull_requests'
1519 __table_args__ = (
1526 __table_args__ = (
1520 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1527 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1521 'mysql_charset': 'utf8'},
1528 'mysql_charset': 'utf8'},
1522 )
1529 )
1523
1530
1524 STATUS_NEW = u'new'
1531 STATUS_NEW = u'new'
1525 STATUS_OPEN = u'open'
1532 STATUS_OPEN = u'open'
1526 STATUS_CLOSED = u'closed'
1533 STATUS_CLOSED = u'closed'
1527
1534
1528 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1535 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1529 title = Column('title', Unicode(256), nullable=True)
1536 title = Column('title', Unicode(256), nullable=True)
1530 description = Column('description', UnicodeText(10240), nullable=True)
1537 description = Column('description', UnicodeText(10240), nullable=True)
1531 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1538 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1532 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1539 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1533 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1540 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1534 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1541 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1535 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1542 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1536 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1543 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1537 org_ref = Column('org_ref', Unicode(256), nullable=False)
1544 org_ref = Column('org_ref', Unicode(256), nullable=False)
1538 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1545 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1539 other_ref = Column('other_ref', Unicode(256), nullable=False)
1546 other_ref = Column('other_ref', Unicode(256), nullable=False)
1540
1547
1541 @hybrid_property
1548 @hybrid_property
1542 def revisions(self):
1549 def revisions(self):
1543 return self._revisions.split(':')
1550 return self._revisions.split(':')
1544
1551
1545 @revisions.setter
1552 @revisions.setter
1546 def revisions(self, val):
1553 def revisions(self, val):
1547 self._revisions = ':'.join(val)
1554 self._revisions = ':'.join(val)
1548
1555
1549 author = relationship('User', lazy='joined')
1556 author = relationship('User', lazy='joined')
1550 reviewers = relationship('PullRequestReviewers',
1557 reviewers = relationship('PullRequestReviewers',
1551 cascade="all, delete, delete-orphan")
1558 cascade="all, delete, delete-orphan")
1552 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1559 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1553 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1560 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1554 statuses = relationship('ChangesetStatus')
1561 statuses = relationship('ChangesetStatus')
1555 comments = relationship('ChangesetComment',
1562 comments = relationship('ChangesetComment',
1556 cascade="all, delete, delete-orphan")
1563 cascade="all, delete, delete-orphan")
1557
1564
1558 def is_closed(self):
1565 def is_closed(self):
1559 return self.status == self.STATUS_CLOSED
1566 return self.status == self.STATUS_CLOSED
1560
1567
1561 def __json__(self):
1568 def __json__(self):
1562 return dict(
1569 return dict(
1563 revisions=self.revisions
1570 revisions=self.revisions
1564 )
1571 )
1565
1572
1566
1573
1567 class PullRequestReviewers(Base, BaseModel):
1574 class PullRequestReviewers(Base, BaseModel):
1568 __tablename__ = 'pull_request_reviewers'
1575 __tablename__ = 'pull_request_reviewers'
1569 __table_args__ = (
1576 __table_args__ = (
1570 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1577 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1571 'mysql_charset': 'utf8'},
1578 'mysql_charset': 'utf8'},
1572 )
1579 )
1573
1580
1574 def __init__(self, user=None, pull_request=None):
1581 def __init__(self, user=None, pull_request=None):
1575 self.user = user
1582 self.user = user
1576 self.pull_request = pull_request
1583 self.pull_request = pull_request
1577
1584
1578 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1585 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1579 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1586 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1580 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1587 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1581
1588
1582 user = relationship('User')
1589 user = relationship('User')
1583 pull_request = relationship('PullRequest')
1590 pull_request = relationship('PullRequest')
1584
1591
1585
1592
1586 class Notification(Base, BaseModel):
1593 class Notification(Base, BaseModel):
1587 __tablename__ = 'notifications'
1594 __tablename__ = 'notifications'
1588 __table_args__ = (
1595 __table_args__ = (
1589 Index('notification_type_idx', 'type'),
1596 Index('notification_type_idx', 'type'),
1590 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1597 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1591 'mysql_charset': 'utf8'},
1598 'mysql_charset': 'utf8'},
1592 )
1599 )
1593
1600
1594 TYPE_CHANGESET_COMMENT = u'cs_comment'
1601 TYPE_CHANGESET_COMMENT = u'cs_comment'
1595 TYPE_MESSAGE = u'message'
1602 TYPE_MESSAGE = u'message'
1596 TYPE_MENTION = u'mention'
1603 TYPE_MENTION = u'mention'
1597 TYPE_REGISTRATION = u'registration'
1604 TYPE_REGISTRATION = u'registration'
1598 TYPE_PULL_REQUEST = u'pull_request'
1605 TYPE_PULL_REQUEST = u'pull_request'
1599 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1606 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1600
1607
1601 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1608 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1602 subject = Column('subject', Unicode(512), nullable=True)
1609 subject = Column('subject', Unicode(512), nullable=True)
1603 body = Column('body', UnicodeText(50000), nullable=True)
1610 body = Column('body', UnicodeText(50000), nullable=True)
1604 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1611 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1605 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1612 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1606 type_ = Column('type', Unicode(256))
1613 type_ = Column('type', Unicode(256))
1607
1614
1608 created_by_user = relationship('User')
1615 created_by_user = relationship('User')
1609 notifications_to_users = relationship('UserNotification', lazy='joined',
1616 notifications_to_users = relationship('UserNotification', lazy='joined',
1610 cascade="all, delete, delete-orphan")
1617 cascade="all, delete, delete-orphan")
1611
1618
1612 @property
1619 @property
1613 def recipients(self):
1620 def recipients(self):
1614 return [x.user for x in UserNotification.query()\
1621 return [x.user for x in UserNotification.query()\
1615 .filter(UserNotification.notification == self)\
1622 .filter(UserNotification.notification == self)\
1616 .order_by(UserNotification.user_id.asc()).all()]
1623 .order_by(UserNotification.user_id.asc()).all()]
1617
1624
1618 @classmethod
1625 @classmethod
1619 def create(cls, created_by, subject, body, recipients, type_=None):
1626 def create(cls, created_by, subject, body, recipients, type_=None):
1620 if type_ is None:
1627 if type_ is None:
1621 type_ = Notification.TYPE_MESSAGE
1628 type_ = Notification.TYPE_MESSAGE
1622
1629
1623 notification = cls()
1630 notification = cls()
1624 notification.created_by_user = created_by
1631 notification.created_by_user = created_by
1625 notification.subject = subject
1632 notification.subject = subject
1626 notification.body = body
1633 notification.body = body
1627 notification.type_ = type_
1634 notification.type_ = type_
1628 notification.created_on = datetime.datetime.now()
1635 notification.created_on = datetime.datetime.now()
1629
1636
1630 for u in recipients:
1637 for u in recipients:
1631 assoc = UserNotification()
1638 assoc = UserNotification()
1632 assoc.notification = notification
1639 assoc.notification = notification
1633 u.notifications.append(assoc)
1640 u.notifications.append(assoc)
1634 Session().add(notification)
1641 Session().add(notification)
1635 return notification
1642 return notification
1636
1643
1637 @property
1644 @property
1638 def description(self):
1645 def description(self):
1639 from rhodecode.model.notification import NotificationModel
1646 from rhodecode.model.notification import NotificationModel
1640 return NotificationModel().make_description(self)
1647 return NotificationModel().make_description(self)
1641
1648
1642
1649
1643 class UserNotification(Base, BaseModel):
1650 class UserNotification(Base, BaseModel):
1644 __tablename__ = 'user_to_notification'
1651 __tablename__ = 'user_to_notification'
1645 __table_args__ = (
1652 __table_args__ = (
1646 UniqueConstraint('user_id', 'notification_id'),
1653 UniqueConstraint('user_id', 'notification_id'),
1647 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1654 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1648 'mysql_charset': 'utf8'}
1655 'mysql_charset': 'utf8'}
1649 )
1656 )
1650 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1657 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1651 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1658 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1652 read = Column('read', Boolean, default=False)
1659 read = Column('read', Boolean, default=False)
1653 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1660 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1654
1661
1655 user = relationship('User', lazy="joined")
1662 user = relationship('User', lazy="joined")
1656 notification = relationship('Notification', lazy="joined",
1663 notification = relationship('Notification', lazy="joined",
1657 order_by=lambda: Notification.created_on.desc(),)
1664 order_by=lambda: Notification.created_on.desc(),)
1658
1665
1659 def mark_as_read(self):
1666 def mark_as_read(self):
1660 self.read = True
1667 self.read = True
1661 Session().add(self)
1668 Session().add(self)
1662
1669
1663
1670
1664 class DbMigrateVersion(Base, BaseModel):
1671 class DbMigrateVersion(Base, BaseModel):
1665 __tablename__ = 'db_migrate_version'
1672 __tablename__ = 'db_migrate_version'
1666 __table_args__ = (
1673 __table_args__ = (
1667 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1674 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1668 'mysql_charset': 'utf8'},
1675 'mysql_charset': 'utf8'},
1669 )
1676 )
1670 repository_id = Column('repository_id', String(250), primary_key=True)
1677 repository_id = Column('repository_id', String(250), primary_key=True)
1671 repository_path = Column('repository_path', Text)
1678 repository_path = Column('repository_path', Text)
1672 version = Column('version', Integer)
1679 version = Column('version', Integer)
@@ -1,310 +1,317 b''
1 """ this is forms validation classes
1 """ this is forms validation classes
2 http://formencode.org/module-formencode.validators.html
2 http://formencode.org/module-formencode.validators.html
3 for list off all availible validators
3 for list off all availible validators
4
4
5 we can create our own validators
5 we can create our own validators
6
6
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
8 pre_validators [] These validators will be applied before the schema
8 pre_validators [] These validators will be applied before the schema
9 chained_validators [] These validators will be applied after the schema
9 chained_validators [] These validators will be applied after the schema
10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
14
14
15
15
16 <name> = formencode.validators.<name of validator>
16 <name> = formencode.validators.<name of validator>
17 <name> must equal form name
17 <name> must equal form name
18 list=[1,2,3,4,5]
18 list=[1,2,3,4,5]
19 for SELECT use formencode.All(OneOf(list), Int())
19 for SELECT use formencode.All(OneOf(list), Int())
20
20
21 """
21 """
22 import logging
22 import logging
23
23
24 import formencode
24 import formencode
25 from formencode import All
25 from formencode import All
26
26
27 from pylons.i18n.translation import _
27 from pylons.i18n.translation import _
28
28
29 from rhodecode.model import validators as v
29 from rhodecode.model import validators as v
30 from rhodecode import BACKENDS
30 from rhodecode import BACKENDS
31
31
32 log = logging.getLogger(__name__)
32 log = logging.getLogger(__name__)
33
33
34
34
35 class LoginForm(formencode.Schema):
35 class LoginForm(formencode.Schema):
36 allow_extra_fields = True
36 allow_extra_fields = True
37 filter_extra_fields = True
37 filter_extra_fields = True
38 username = v.UnicodeString(
38 username = v.UnicodeString(
39 strip=True,
39 strip=True,
40 min=1,
40 min=1,
41 not_empty=True,
41 not_empty=True,
42 messages={
42 messages={
43 'empty': _(u'Please enter a login'),
43 'empty': _(u'Please enter a login'),
44 'tooShort': _(u'Enter a value %(min)i characters long or more')}
44 'tooShort': _(u'Enter a value %(min)i characters long or more')}
45 )
45 )
46
46
47 password = v.UnicodeString(
47 password = v.UnicodeString(
48 strip=False,
48 strip=False,
49 min=3,
49 min=3,
50 not_empty=True,
50 not_empty=True,
51 messages={
51 messages={
52 'empty': _(u'Please enter a password'),
52 'empty': _(u'Please enter a password'),
53 'tooShort': _(u'Enter %(min)i characters or more')}
53 'tooShort': _(u'Enter %(min)i characters or more')}
54 )
54 )
55
55
56 remember = v.StringBoolean(if_missing=False)
56 remember = v.StringBoolean(if_missing=False)
57
57
58 chained_validators = [v.ValidAuth()]
58 chained_validators = [v.ValidAuth()]
59
59
60
60
61 def UserForm(edit=False, old_data={}):
61 def UserForm(edit=False, old_data={}):
62 class _UserForm(formencode.Schema):
62 class _UserForm(formencode.Schema):
63 allow_extra_fields = True
63 allow_extra_fields = True
64 filter_extra_fields = True
64 filter_extra_fields = True
65 username = All(v.UnicodeString(strip=True, min=1, not_empty=True),
65 username = All(v.UnicodeString(strip=True, min=1, not_empty=True),
66 v.ValidUsername(edit, old_data))
66 v.ValidUsername(edit, old_data))
67 if edit:
67 if edit:
68 new_password = All(
68 new_password = All(
69 v.ValidPassword(),
69 v.ValidPassword(),
70 v.UnicodeString(strip=False, min=6, not_empty=False)
70 v.UnicodeString(strip=False, min=6, not_empty=False)
71 )
71 )
72 password_confirmation = All(
72 password_confirmation = All(
73 v.ValidPassword(),
73 v.ValidPassword(),
74 v.UnicodeString(strip=False, min=6, not_empty=False),
74 v.UnicodeString(strip=False, min=6, not_empty=False),
75 )
75 )
76 admin = v.StringBoolean(if_missing=False)
76 admin = v.StringBoolean(if_missing=False)
77 else:
77 else:
78 password = All(
78 password = All(
79 v.ValidPassword(),
79 v.ValidPassword(),
80 v.UnicodeString(strip=False, min=6, not_empty=True)
80 v.UnicodeString(strip=False, min=6, not_empty=True)
81 )
81 )
82 password_confirmation = All(
82 password_confirmation = All(
83 v.ValidPassword(),
83 v.ValidPassword(),
84 v.UnicodeString(strip=False, min=6, not_empty=False)
84 v.UnicodeString(strip=False, min=6, not_empty=False)
85 )
85 )
86
86
87 active = v.StringBoolean(if_missing=False)
87 active = v.StringBoolean(if_missing=False)
88 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
88 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
89 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
89 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
90 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
90 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
91
91
92 chained_validators = [v.ValidPasswordsMatch()]
92 chained_validators = [v.ValidPasswordsMatch()]
93
93
94 return _UserForm
94 return _UserForm
95
95
96
96
97 def UsersGroupForm(edit=False, old_data={}, available_members=[]):
97 def UsersGroupForm(edit=False, old_data={}, available_members=[]):
98 class _UsersGroupForm(formencode.Schema):
98 class _UsersGroupForm(formencode.Schema):
99 allow_extra_fields = True
99 allow_extra_fields = True
100 filter_extra_fields = True
100 filter_extra_fields = True
101
101
102 users_group_name = All(
102 users_group_name = All(
103 v.UnicodeString(strip=True, min=1, not_empty=True),
103 v.UnicodeString(strip=True, min=1, not_empty=True),
104 v.ValidUsersGroup(edit, old_data)
104 v.ValidUsersGroup(edit, old_data)
105 )
105 )
106
106
107 users_group_active = v.StringBoolean(if_missing=False)
107 users_group_active = v.StringBoolean(if_missing=False)
108
108
109 if edit:
109 if edit:
110 users_group_members = v.OneOf(
110 users_group_members = v.OneOf(
111 available_members, hideList=False, testValueList=True,
111 available_members, hideList=False, testValueList=True,
112 if_missing=None, not_empty=False
112 if_missing=None, not_empty=False
113 )
113 )
114
114
115 return _UsersGroupForm
115 return _UsersGroupForm
116
116
117
117
118 def ReposGroupForm(edit=False, old_data={}, available_groups=[]):
118 def ReposGroupForm(edit=False, old_data={}, available_groups=[]):
119 class _ReposGroupForm(formencode.Schema):
119 class _ReposGroupForm(formencode.Schema):
120 allow_extra_fields = True
120 allow_extra_fields = True
121 filter_extra_fields = False
121 filter_extra_fields = False
122
122
123 group_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
123 group_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
124 v.SlugifyName())
124 v.SlugifyName())
125 group_description = v.UnicodeString(strip=True, min=1,
125 group_description = v.UnicodeString(strip=True, min=1,
126 not_empty=True)
126 not_empty=True)
127 group_parent_id = v.OneOf(available_groups, hideList=False,
127 group_parent_id = v.OneOf(available_groups, hideList=False,
128 testValueList=True,
128 testValueList=True,
129 if_missing=None, not_empty=False)
129 if_missing=None, not_empty=False)
130
130
131 chained_validators = [v.ValidReposGroup(edit, old_data),
131 chained_validators = [v.ValidReposGroup(edit, old_data),
132 v.ValidPerms('group')]
132 v.ValidPerms('group')]
133
133
134 return _ReposGroupForm
134 return _ReposGroupForm
135
135
136
136
137 def RegisterForm(edit=False, old_data={}):
137 def RegisterForm(edit=False, old_data={}):
138 class _RegisterForm(formencode.Schema):
138 class _RegisterForm(formencode.Schema):
139 allow_extra_fields = True
139 allow_extra_fields = True
140 filter_extra_fields = True
140 filter_extra_fields = True
141 username = All(
141 username = All(
142 v.ValidUsername(edit, old_data),
142 v.ValidUsername(edit, old_data),
143 v.UnicodeString(strip=True, min=1, not_empty=True)
143 v.UnicodeString(strip=True, min=1, not_empty=True)
144 )
144 )
145 password = All(
145 password = All(
146 v.ValidPassword(),
146 v.ValidPassword(),
147 v.UnicodeString(strip=False, min=6, not_empty=True)
147 v.UnicodeString(strip=False, min=6, not_empty=True)
148 )
148 )
149 password_confirmation = All(
149 password_confirmation = All(
150 v.ValidPassword(),
150 v.ValidPassword(),
151 v.UnicodeString(strip=False, min=6, not_empty=True)
151 v.UnicodeString(strip=False, min=6, not_empty=True)
152 )
152 )
153 active = v.StringBoolean(if_missing=False)
153 active = v.StringBoolean(if_missing=False)
154 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
154 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
155 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
155 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
156 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
156 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
157
157
158 chained_validators = [v.ValidPasswordsMatch()]
158 chained_validators = [v.ValidPasswordsMatch()]
159
159
160 return _RegisterForm
160 return _RegisterForm
161
161
162
162
163 def PasswordResetForm():
163 def PasswordResetForm():
164 class _PasswordResetForm(formencode.Schema):
164 class _PasswordResetForm(formencode.Schema):
165 allow_extra_fields = True
165 allow_extra_fields = True
166 filter_extra_fields = True
166 filter_extra_fields = True
167 email = All(v.ValidSystemEmail(), v.Email(not_empty=True))
167 email = All(v.ValidSystemEmail(), v.Email(not_empty=True))
168 return _PasswordResetForm
168 return _PasswordResetForm
169
169
170
170
171 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
171 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
172 repo_groups=[], landing_revs=[]):
172 repo_groups=[], landing_revs=[]):
173 class _RepoForm(formencode.Schema):
173 class _RepoForm(formencode.Schema):
174 allow_extra_fields = True
174 allow_extra_fields = True
175 filter_extra_fields = False
175 filter_extra_fields = False
176 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
176 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
177 v.SlugifyName())
177 v.SlugifyName())
178 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
178 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
179 repo_group = v.OneOf(repo_groups, hideList=True)
179 repo_group = v.OneOf(repo_groups, hideList=True)
180 repo_type = v.OneOf(supported_backends)
180 repo_type = v.OneOf(supported_backends)
181 description = v.UnicodeString(strip=True, min=1, not_empty=False)
181 description = v.UnicodeString(strip=True, min=1, not_empty=False)
182 private = v.StringBoolean(if_missing=False)
182 private = v.StringBoolean(if_missing=False)
183 enable_statistics = v.StringBoolean(if_missing=False)
183 enable_statistics = v.StringBoolean(if_missing=False)
184 enable_downloads = v.StringBoolean(if_missing=False)
184 enable_downloads = v.StringBoolean(if_missing=False)
185 landing_rev = v.OneOf(landing_revs, hideList=True)
185 landing_rev = v.OneOf(landing_revs, hideList=True)
186
186
187 if edit:
187 if edit:
188 #this is repo owner
188 #this is repo owner
189 user = All(v.UnicodeString(not_empty=True), v.ValidRepoUser())
189 user = All(v.UnicodeString(not_empty=True), v.ValidRepoUser())
190
190
191 chained_validators = [v.ValidCloneUri(),
191 chained_validators = [v.ValidCloneUri(),
192 v.ValidRepoName(edit, old_data),
192 v.ValidRepoName(edit, old_data),
193 v.ValidPerms()]
193 v.ValidPerms()]
194 return _RepoForm
194 return _RepoForm
195
195
196
196
197 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
197 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
198 repo_groups=[], landing_revs=[]):
198 repo_groups=[], landing_revs=[]):
199 class _RepoForkForm(formencode.Schema):
199 class _RepoForkForm(formencode.Schema):
200 allow_extra_fields = True
200 allow_extra_fields = True
201 filter_extra_fields = False
201 filter_extra_fields = False
202 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
202 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
203 v.SlugifyName())
203 v.SlugifyName())
204 repo_group = v.OneOf(repo_groups, hideList=True)
204 repo_group = v.OneOf(repo_groups, hideList=True)
205 repo_type = All(v.ValidForkType(old_data), v.OneOf(supported_backends))
205 repo_type = All(v.ValidForkType(old_data), v.OneOf(supported_backends))
206 description = v.UnicodeString(strip=True, min=1, not_empty=True)
206 description = v.UnicodeString(strip=True, min=1, not_empty=True)
207 private = v.StringBoolean(if_missing=False)
207 private = v.StringBoolean(if_missing=False)
208 copy_permissions = v.StringBoolean(if_missing=False)
208 copy_permissions = v.StringBoolean(if_missing=False)
209 update_after_clone = v.StringBoolean(if_missing=False)
209 update_after_clone = v.StringBoolean(if_missing=False)
210 fork_parent_id = v.UnicodeString()
210 fork_parent_id = v.UnicodeString()
211 chained_validators = [v.ValidForkName(edit, old_data)]
211 chained_validators = [v.ValidForkName(edit, old_data)]
212 landing_rev = v.OneOf(landing_revs, hideList=True)
212 landing_rev = v.OneOf(landing_revs, hideList=True)
213
213
214 return _RepoForkForm
214 return _RepoForkForm
215
215
216
216
217 def RepoSettingsForm(edit=False, old_data={},
217 def RepoSettingsForm(edit=False, old_data={},
218 supported_backends=BACKENDS.keys(), repo_groups=[],
218 supported_backends=BACKENDS.keys(), repo_groups=[],
219 landing_revs=[]):
219 landing_revs=[]):
220 class _RepoForm(formencode.Schema):
220 class _RepoForm(formencode.Schema):
221 allow_extra_fields = True
221 allow_extra_fields = True
222 filter_extra_fields = False
222 filter_extra_fields = False
223 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
223 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
224 v.SlugifyName())
224 v.SlugifyName())
225 description = v.UnicodeString(strip=True, min=1, not_empty=True)
225 description = v.UnicodeString(strip=True, min=1, not_empty=True)
226 repo_group = v.OneOf(repo_groups, hideList=True)
226 repo_group = v.OneOf(repo_groups, hideList=True)
227 private = v.StringBoolean(if_missing=False)
227 private = v.StringBoolean(if_missing=False)
228 landing_rev = v.OneOf(landing_revs, hideList=True)
228 landing_rev = v.OneOf(landing_revs, hideList=True)
229 chained_validators = [v.ValidRepoName(edit, old_data), v.ValidPerms(),
229 chained_validators = [v.ValidRepoName(edit, old_data), v.ValidPerms(),
230 v.ValidSettings()]
230 v.ValidSettings()]
231 return _RepoForm
231 return _RepoForm
232
232
233
233
234 def ApplicationSettingsForm():
234 def ApplicationSettingsForm():
235 class _ApplicationSettingsForm(formencode.Schema):
235 class _ApplicationSettingsForm(formencode.Schema):
236 allow_extra_fields = True
236 allow_extra_fields = True
237 filter_extra_fields = False
237 filter_extra_fields = False
238 rhodecode_title = v.UnicodeString(strip=True, min=1, not_empty=True)
238 rhodecode_title = v.UnicodeString(strip=True, min=1, not_empty=True)
239 rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True)
239 rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True)
240 rhodecode_ga_code = v.UnicodeString(strip=True, min=1, not_empty=False)
240 rhodecode_ga_code = v.UnicodeString(strip=True, min=1, not_empty=False)
241
241
242 return _ApplicationSettingsForm
242 return _ApplicationSettingsForm
243
243
244
244
245 def ApplicationVisualisationForm():
246 class _ApplicationVisualisationForm(formencode.Schema):
247 allow_extra_fields = True
248 filter_extra_fields = False
249 rhodecode_show_public_icon = v.StringBoolean(if_missing=False)
250 rhodecode_show_private_icon = v.StringBoolean(if_missing=False)
251 rhodecode_stylify_metatags = v.StringBoolean(if_missing=False)
252
253 return _ApplicationVisualisationForm
254
255
245 def ApplicationUiSettingsForm():
256 def ApplicationUiSettingsForm():
246 class _ApplicationUiSettingsForm(formencode.Schema):
257 class _ApplicationUiSettingsForm(formencode.Schema):
247 allow_extra_fields = True
258 allow_extra_fields = True
248 filter_extra_fields = False
259 filter_extra_fields = False
249 web_push_ssl = v.OneOf(['true', 'false'], if_missing='false')
260 web_push_ssl = v.StringBoolean(if_missing=False)
250 paths_root_path = All(
261 paths_root_path = All(
251 v.ValidPath(),
262 v.ValidPath(),
252 v.UnicodeString(strip=True, min=1, not_empty=True)
263 v.UnicodeString(strip=True, min=1, not_empty=True)
253 )
264 )
254 hooks_changegroup_update = v.OneOf(['True', 'False'],
265 hooks_changegroup_update = v.StringBoolean(if_missing=False)
255 if_missing=False)
266 hooks_changegroup_repo_size = v.StringBoolean(if_missing=False)
256 hooks_changegroup_repo_size = v.OneOf(['True', 'False'],
267 hooks_changegroup_push_logger = v.StringBoolean(if_missing=False)
257 if_missing=False)
268 hooks_preoutgoing_pull_logger = v.StringBoolean(if_missing=False)
258 hooks_changegroup_push_logger = v.OneOf(['True', 'False'],
259 if_missing=False)
260 hooks_preoutgoing_pull_logger = v.OneOf(['True', 'False'],
261 if_missing=False)
262
269
263 return _ApplicationUiSettingsForm
270 return _ApplicationUiSettingsForm
264
271
265
272
266 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
273 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
267 class _DefaultPermissionsForm(formencode.Schema):
274 class _DefaultPermissionsForm(formencode.Schema):
268 allow_extra_fields = True
275 allow_extra_fields = True
269 filter_extra_fields = True
276 filter_extra_fields = True
270 overwrite_default = v.StringBoolean(if_missing=False)
277 overwrite_default = v.StringBoolean(if_missing=False)
271 anonymous = v.OneOf(['True', 'False'], if_missing=False)
278 anonymous = v.StringBoolean(if_missing=False)
272 default_perm = v.OneOf(perms_choices)
279 default_perm = v.OneOf(perms_choices)
273 default_register = v.OneOf(register_choices)
280 default_register = v.OneOf(register_choices)
274 default_create = v.OneOf(create_choices)
281 default_create = v.OneOf(create_choices)
275
282
276 return _DefaultPermissionsForm
283 return _DefaultPermissionsForm
277
284
278
285
279 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices,
286 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices,
280 tls_kind_choices):
287 tls_kind_choices):
281 class _LdapSettingsForm(formencode.Schema):
288 class _LdapSettingsForm(formencode.Schema):
282 allow_extra_fields = True
289 allow_extra_fields = True
283 filter_extra_fields = True
290 filter_extra_fields = True
284 #pre_validators = [LdapLibValidator]
291 #pre_validators = [LdapLibValidator]
285 ldap_active = v.StringBoolean(if_missing=False)
292 ldap_active = v.StringBoolean(if_missing=False)
286 ldap_host = v.UnicodeString(strip=True,)
293 ldap_host = v.UnicodeString(strip=True,)
287 ldap_port = v.Number(strip=True,)
294 ldap_port = v.Number(strip=True,)
288 ldap_tls_kind = v.OneOf(tls_kind_choices)
295 ldap_tls_kind = v.OneOf(tls_kind_choices)
289 ldap_tls_reqcert = v.OneOf(tls_reqcert_choices)
296 ldap_tls_reqcert = v.OneOf(tls_reqcert_choices)
290 ldap_dn_user = v.UnicodeString(strip=True,)
297 ldap_dn_user = v.UnicodeString(strip=True,)
291 ldap_dn_pass = v.UnicodeString(strip=True,)
298 ldap_dn_pass = v.UnicodeString(strip=True,)
292 ldap_base_dn = v.UnicodeString(strip=True,)
299 ldap_base_dn = v.UnicodeString(strip=True,)
293 ldap_filter = v.UnicodeString(strip=True,)
300 ldap_filter = v.UnicodeString(strip=True,)
294 ldap_search_scope = v.OneOf(search_scope_choices)
301 ldap_search_scope = v.OneOf(search_scope_choices)
295 ldap_attr_login = All(
302 ldap_attr_login = All(
296 v.AttrLoginValidator(),
303 v.AttrLoginValidator(),
297 v.UnicodeString(strip=True,)
304 v.UnicodeString(strip=True,)
298 )
305 )
299 ldap_attr_firstname = v.UnicodeString(strip=True,)
306 ldap_attr_firstname = v.UnicodeString(strip=True,)
300 ldap_attr_lastname = v.UnicodeString(strip=True,)
307 ldap_attr_lastname = v.UnicodeString(strip=True,)
301 ldap_attr_email = v.UnicodeString(strip=True,)
308 ldap_attr_email = v.UnicodeString(strip=True,)
302
309
303 return _LdapSettingsForm
310 return _LdapSettingsForm
304
311
305
312
306 def UserExtraEmailForm():
313 def UserExtraEmailForm():
307 class _UserExtraEmailForm(formencode.Schema):
314 class _UserExtraEmailForm(formencode.Schema):
308 email = All(v.UniqSystemEmail(), v.Email)
315 email = All(v.UniqSystemEmail(), v.Email)
309
316
310 return _UserExtraEmailForm
317 return _UserExtraEmailForm
@@ -1,4560 +1,4635 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 {
2 {
3 border: 0;
3 border: 0;
4 outline: 0;
4 outline: 0;
5 font-size: 100%;
5 font-size: 100%;
6 vertical-align: baseline;
6 vertical-align: baseline;
7 background: transparent;
7 background: transparent;
8 margin: 0;
8 margin: 0;
9 padding: 0;
9 padding: 0;
10 }
10 }
11
11
12 body {
12 body {
13 line-height: 1;
13 line-height: 1;
14 height: 100%;
14 height: 100%;
15 background: url("../images/background.png") repeat scroll 0 0 #B0B0B0;
15 background: url("../images/background.png") repeat scroll 0 0 #B0B0B0;
16 font-family: Lucida Grande, Verdana, Lucida Sans Regular,
16 font-family: Lucida Grande, Verdana, Lucida Sans Regular,
17 Lucida Sans Unicode, Arial, sans-serif; font-size : 12px;
17 Lucida Sans Unicode, Arial, sans-serif; font-size : 12px;
18 color: #000;
18 color: #000;
19 margin: 0;
19 margin: 0;
20 padding: 0;
20 padding: 0;
21 font-size: 12px;
21 font-size: 12px;
22 }
22 }
23
23
24 ol,ul {
24 ol,ul {
25 list-style: none;
25 list-style: none;
26 }
26 }
27
27
28 blockquote,q {
28 blockquote,q {
29 quotes: none;
29 quotes: none;
30 }
30 }
31
31
32 blockquote:before,blockquote:after,q:before,q:after {
32 blockquote:before,blockquote:after,q:before,q:after {
33 content: none;
33 content: none;
34 }
34 }
35
35
36 :focus {
36 :focus {
37 outline: 0;
37 outline: 0;
38 }
38 }
39
39
40 del {
40 del {
41 text-decoration: line-through;
41 text-decoration: line-through;
42 }
42 }
43
43
44 table {
44 table {
45 border-collapse: collapse;
45 border-collapse: collapse;
46 border-spacing: 0;
46 border-spacing: 0;
47 }
47 }
48
48
49 html {
49 html {
50 height: 100%;
50 height: 100%;
51 }
51 }
52
52
53 a {
53 a {
54 color: #003367;
54 color: #003367;
55 text-decoration: none;
55 text-decoration: none;
56 cursor: pointer;
56 cursor: pointer;
57 }
57 }
58
58
59 a:hover {
59 a:hover {
60 color: #316293;
60 color: #316293;
61 text-decoration: underline;
61 text-decoration: underline;
62 }
62 }
63
63
64 h1,h2,h3,h4,h5,h6 {
64 h1,h2,h3,h4,h5,h6 {
65 color: #292929;
65 color: #292929;
66 font-weight: 700;
66 font-weight: 700;
67 }
67 }
68
68
69 h1 {
69 h1 {
70 font-size: 22px;
70 font-size: 22px;
71 }
71 }
72
72
73 h2 {
73 h2 {
74 font-size: 20px;
74 font-size: 20px;
75 }
75 }
76
76
77 h3 {
77 h3 {
78 font-size: 18px;
78 font-size: 18px;
79 }
79 }
80
80
81 h4 {
81 h4 {
82 font-size: 16px;
82 font-size: 16px;
83 }
83 }
84
84
85 h5 {
85 h5 {
86 font-size: 14px;
86 font-size: 14px;
87 }
87 }
88
88
89 h6 {
89 h6 {
90 font-size: 11px;
90 font-size: 11px;
91 }
91 }
92
92
93 ul.circle {
93 ul.circle {
94 list-style-type: circle;
94 list-style-type: circle;
95 }
95 }
96
96
97 ul.disc {
97 ul.disc {
98 list-style-type: disc;
98 list-style-type: disc;
99 }
99 }
100
100
101 ul.square {
101 ul.square {
102 list-style-type: square;
102 list-style-type: square;
103 }
103 }
104
104
105 ol.lower-roman {
105 ol.lower-roman {
106 list-style-type: lower-roman;
106 list-style-type: lower-roman;
107 }
107 }
108
108
109 ol.upper-roman {
109 ol.upper-roman {
110 list-style-type: upper-roman;
110 list-style-type: upper-roman;
111 }
111 }
112
112
113 ol.lower-alpha {
113 ol.lower-alpha {
114 list-style-type: lower-alpha;
114 list-style-type: lower-alpha;
115 }
115 }
116
116
117 ol.upper-alpha {
117 ol.upper-alpha {
118 list-style-type: upper-alpha;
118 list-style-type: upper-alpha;
119 }
119 }
120
120
121 ol.decimal {
121 ol.decimal {
122 list-style-type: decimal;
122 list-style-type: decimal;
123 }
123 }
124
124
125 div.color {
125 div.color {
126 clear: both;
126 clear: both;
127 overflow: hidden;
127 overflow: hidden;
128 position: absolute;
128 position: absolute;
129 background: #FFF;
129 background: #FFF;
130 margin: 7px 0 0 60px;
130 margin: 7px 0 0 60px;
131 padding: 1px 1px 1px 0;
131 padding: 1px 1px 1px 0;
132 }
132 }
133
133
134 div.color a {
134 div.color a {
135 width: 15px;
135 width: 15px;
136 height: 15px;
136 height: 15px;
137 display: block;
137 display: block;
138 float: left;
138 float: left;
139 margin: 0 0 0 1px;
139 margin: 0 0 0 1px;
140 padding: 0;
140 padding: 0;
141 }
141 }
142
142
143 div.options {
143 div.options {
144 clear: both;
144 clear: both;
145 overflow: hidden;
145 overflow: hidden;
146 position: absolute;
146 position: absolute;
147 background: #FFF;
147 background: #FFF;
148 margin: 7px 0 0 162px;
148 margin: 7px 0 0 162px;
149 padding: 0;
149 padding: 0;
150 }
150 }
151
151
152 div.options a {
152 div.options a {
153 height: 1%;
153 height: 1%;
154 display: block;
154 display: block;
155 text-decoration: none;
155 text-decoration: none;
156 margin: 0;
156 margin: 0;
157 padding: 3px 8px;
157 padding: 3px 8px;
158 }
158 }
159
159
160 .top-left-rounded-corner {
160 .top-left-rounded-corner {
161 -webkit-border-top-left-radius: 8px;
161 -webkit-border-top-left-radius: 8px;
162 -khtml-border-radius-topleft: 8px;
162 -khtml-border-radius-topleft: 8px;
163 -moz-border-radius-topleft: 8px;
163 -moz-border-radius-topleft: 8px;
164 border-top-left-radius: 8px;
164 border-top-left-radius: 8px;
165 }
165 }
166
166
167 .top-right-rounded-corner {
167 .top-right-rounded-corner {
168 -webkit-border-top-right-radius: 8px;
168 -webkit-border-top-right-radius: 8px;
169 -khtml-border-radius-topright: 8px;
169 -khtml-border-radius-topright: 8px;
170 -moz-border-radius-topright: 8px;
170 -moz-border-radius-topright: 8px;
171 border-top-right-radius: 8px;
171 border-top-right-radius: 8px;
172 }
172 }
173
173
174 .bottom-left-rounded-corner {
174 .bottom-left-rounded-corner {
175 -webkit-border-bottom-left-radius: 8px;
175 -webkit-border-bottom-left-radius: 8px;
176 -khtml-border-radius-bottomleft: 8px;
176 -khtml-border-radius-bottomleft: 8px;
177 -moz-border-radius-bottomleft: 8px;
177 -moz-border-radius-bottomleft: 8px;
178 border-bottom-left-radius: 8px;
178 border-bottom-left-radius: 8px;
179 }
179 }
180
180
181 .bottom-right-rounded-corner {
181 .bottom-right-rounded-corner {
182 -webkit-border-bottom-right-radius: 8px;
182 -webkit-border-bottom-right-radius: 8px;
183 -khtml-border-radius-bottomright: 8px;
183 -khtml-border-radius-bottomright: 8px;
184 -moz-border-radius-bottomright: 8px;
184 -moz-border-radius-bottomright: 8px;
185 border-bottom-right-radius: 8px;
185 border-bottom-right-radius: 8px;
186 }
186 }
187
187
188 .top-left-rounded-corner-mid {
188 .top-left-rounded-corner-mid {
189 -webkit-border-top-left-radius: 4px;
189 -webkit-border-top-left-radius: 4px;
190 -khtml-border-radius-topleft: 4px;
190 -khtml-border-radius-topleft: 4px;
191 -moz-border-radius-topleft: 4px;
191 -moz-border-radius-topleft: 4px;
192 border-top-left-radius: 4px;
192 border-top-left-radius: 4px;
193 }
193 }
194
194
195 .top-right-rounded-corner-mid {
195 .top-right-rounded-corner-mid {
196 -webkit-border-top-right-radius: 4px;
196 -webkit-border-top-right-radius: 4px;
197 -khtml-border-radius-topright: 4px;
197 -khtml-border-radius-topright: 4px;
198 -moz-border-radius-topright: 4px;
198 -moz-border-radius-topright: 4px;
199 border-top-right-radius: 4px;
199 border-top-right-radius: 4px;
200 }
200 }
201
201
202 .bottom-left-rounded-corner-mid {
202 .bottom-left-rounded-corner-mid {
203 -webkit-border-bottom-left-radius: 4px;
203 -webkit-border-bottom-left-radius: 4px;
204 -khtml-border-radius-bottomleft: 4px;
204 -khtml-border-radius-bottomleft: 4px;
205 -moz-border-radius-bottomleft: 4px;
205 -moz-border-radius-bottomleft: 4px;
206 border-bottom-left-radius: 4px;
206 border-bottom-left-radius: 4px;
207 }
207 }
208
208
209 .bottom-right-rounded-corner-mid {
209 .bottom-right-rounded-corner-mid {
210 -webkit-border-bottom-right-radius: 4px;
210 -webkit-border-bottom-right-radius: 4px;
211 -khtml-border-radius-bottomright: 4px;
211 -khtml-border-radius-bottomright: 4px;
212 -moz-border-radius-bottomright: 4px;
212 -moz-border-radius-bottomright: 4px;
213 border-bottom-right-radius: 4px;
213 border-bottom-right-radius: 4px;
214 }
214 }
215
215
216 .help-block {
216 .help-block {
217 color: #999999;
217 color: #999999;
218 display: block;
218 display: block;
219 margin-bottom: 0;
219 margin-bottom: 0;
220 margin-top: 5px;
220 margin-top: 5px;
221 }
221 }
222 a.permalink{
222 a.permalink{
223 visibility: hidden;
223 visibility: hidden;
224 }
224 }
225
225
226 a.permalink:hover{
226 a.permalink:hover{
227 text-decoration: none;
227 text-decoration: none;
228 }
228 }
229
229
230 h1:hover > a.permalink,
230 h1:hover > a.permalink,
231 h2:hover > a.permalink,
231 h2:hover > a.permalink,
232 h3:hover > a.permalink,
232 h3:hover > a.permalink,
233 h4:hover > a.permalink,
233 h4:hover > a.permalink,
234 h5:hover > a.permalink,
234 h5:hover > a.permalink,
235 h6:hover > a.permalink,
235 h6:hover > a.permalink,
236 div:hover > a.permalink {
236 div:hover > a.permalink {
237 visibility: visible;
237 visibility: visible;
238 }
238 }
239
239
240 #header {
240 #header {
241 margin: 0;
241 margin: 0;
242 padding: 0 10px;
242 padding: 0 10px;
243 }
243 }
244
244
245 #header ul#logged-user {
245 #header ul#logged-user {
246 margin-bottom: 5px !important;
246 margin-bottom: 5px !important;
247 -webkit-border-radius: 0px 0px 8px 8px;
247 -webkit-border-radius: 0px 0px 8px 8px;
248 -khtml-border-radius: 0px 0px 8px 8px;
248 -khtml-border-radius: 0px 0px 8px 8px;
249 -moz-border-radius: 0px 0px 8px 8px;
249 -moz-border-radius: 0px 0px 8px 8px;
250 border-radius: 0px 0px 8px 8px;
250 border-radius: 0px 0px 8px 8px;
251 height: 37px;
251 height: 37px;
252 background-color: #003B76;
252 background-color: #003B76;
253 background-repeat: repeat-x;
253 background-repeat: repeat-x;
254 background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
254 background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
255 background-image: -moz-linear-gradient(top, #003b76, #00376e);
255 background-image: -moz-linear-gradient(top, #003b76, #00376e);
256 background-image: -ms-linear-gradient(top, #003b76, #00376e);
256 background-image: -ms-linear-gradient(top, #003b76, #00376e);
257 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76), color-stop(100%, #00376e) );
257 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76), color-stop(100%, #00376e) );
258 background-image: -webkit-linear-gradient(top, #003b76, #00376e);
258 background-image: -webkit-linear-gradient(top, #003b76, #00376e);
259 background-image: -o-linear-gradient(top, #003b76, #00376e);
259 background-image: -o-linear-gradient(top, #003b76, #00376e);
260 background-image: linear-gradient(top, #003b76, #00376e);
260 background-image: linear-gradient(top, #003b76, #00376e);
261 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',endColorstr='#00376e', GradientType=0 );
261 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',endColorstr='#00376e', GradientType=0 );
262 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
262 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
263 }
263 }
264
264
265 #header ul#logged-user li {
265 #header ul#logged-user li {
266 list-style: none;
266 list-style: none;
267 float: left;
267 float: left;
268 margin: 8px 0 0;
268 margin: 8px 0 0;
269 padding: 4px 12px;
269 padding: 4px 12px;
270 border-left: 1px solid #316293;
270 border-left: 1px solid #316293;
271 }
271 }
272
272
273 #header ul#logged-user li.first {
273 #header ul#logged-user li.first {
274 border-left: none;
274 border-left: none;
275 margin: 4px;
275 margin: 4px;
276 }
276 }
277
277
278 #header ul#logged-user li.first div.gravatar {
278 #header ul#logged-user li.first div.gravatar {
279 margin-top: -2px;
279 margin-top: -2px;
280 }
280 }
281
281
282 #header ul#logged-user li.first div.account {
282 #header ul#logged-user li.first div.account {
283 padding-top: 4px;
283 padding-top: 4px;
284 float: left;
284 float: left;
285 }
285 }
286
286
287 #header ul#logged-user li.last {
287 #header ul#logged-user li.last {
288 border-right: none;
288 border-right: none;
289 }
289 }
290
290
291 #header ul#logged-user li a {
291 #header ul#logged-user li a {
292 color: #fff;
292 color: #fff;
293 font-weight: 700;
293 font-weight: 700;
294 text-decoration: none;
294 text-decoration: none;
295 }
295 }
296
296
297 #header ul#logged-user li a:hover {
297 #header ul#logged-user li a:hover {
298 text-decoration: underline;
298 text-decoration: underline;
299 }
299 }
300
300
301 #header ul#logged-user li.highlight a {
301 #header ul#logged-user li.highlight a {
302 color: #fff;
302 color: #fff;
303 }
303 }
304
304
305 #header ul#logged-user li.highlight a:hover {
305 #header ul#logged-user li.highlight a:hover {
306 color: #FFF;
306 color: #FFF;
307 }
307 }
308
308
309 #header #header-inner {
309 #header #header-inner {
310 min-height: 44px;
310 min-height: 44px;
311 clear: both;
311 clear: both;
312 position: relative;
312 position: relative;
313 background-color: #003B76;
313 background-color: #003B76;
314 background-repeat: repeat-x;
314 background-repeat: repeat-x;
315 background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
315 background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
316 background-image: -moz-linear-gradient(top, #003b76, #00376e);
316 background-image: -moz-linear-gradient(top, #003b76, #00376e);
317 background-image: -ms-linear-gradient(top, #003b76, #00376e);
317 background-image: -ms-linear-gradient(top, #003b76, #00376e);
318 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76),color-stop(100%, #00376e) );
318 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76),color-stop(100%, #00376e) );
319 background-image: -webkit-linear-gradient(top, #003b76, #00376e);
319 background-image: -webkit-linear-gradient(top, #003b76, #00376e);
320 background-image: -o-linear-gradient(top, #003b76, #00376e);
320 background-image: -o-linear-gradient(top, #003b76, #00376e);
321 background-image: linear-gradient(top, #003b76, #00376e);
321 background-image: linear-gradient(top, #003b76, #00376e);
322 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',endColorstr='#00376e', GradientType=0 );
322 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',endColorstr='#00376e', GradientType=0 );
323 margin: 0;
323 margin: 0;
324 padding: 0;
324 padding: 0;
325 display: block;
325 display: block;
326 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
326 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
327 -webkit-border-radius: 4px 4px 4px 4px;
327 -webkit-border-radius: 4px 4px 4px 4px;
328 -khtml-border-radius: 4px 4px 4px 4px;
328 -khtml-border-radius: 4px 4px 4px 4px;
329 -moz-border-radius: 4px 4px 4px 4px;
329 -moz-border-radius: 4px 4px 4px 4px;
330 border-radius: 4px 4px 4px 4px;
330 border-radius: 4px 4px 4px 4px;
331 }
331 }
332 #header #header-inner.hover{
332 #header #header-inner.hover{
333 position: fixed !important;
333 position: fixed !important;
334 width: 100% !important;
334 width: 100% !important;
335 margin-left: -10px !important;
335 margin-left: -10px !important;
336 z-index: 10000;
336 z-index: 10000;
337 -webkit-border-radius: 0px 0px 0px 0px;
337 -webkit-border-radius: 0px 0px 0px 0px;
338 -khtml-border-radius: 0px 0px 0px 0px;
338 -khtml-border-radius: 0px 0px 0px 0px;
339 -moz-border-radius: 0px 0px 0px 0px;
339 -moz-border-radius: 0px 0px 0px 0px;
340 border-radius: 0px 0px 0px 0px;
340 border-radius: 0px 0px 0px 0px;
341 }
341 }
342
342
343 .ie7 #header #header-inner.hover,
343 .ie7 #header #header-inner.hover,
344 .ie8 #header #header-inner.hover,
344 .ie8 #header #header-inner.hover,
345 .ie9 #header #header-inner.hover
345 .ie9 #header #header-inner.hover
346 {
346 {
347 z-index: auto !important;
347 z-index: auto !important;
348 }
348 }
349
349
350 .header-pos-fix{
350 .header-pos-fix{
351 margin-top: -44px;
351 margin-top: -44px;
352 padding-top: 44px;
352 padding-top: 44px;
353 }
353 }
354
354
355 #header #header-inner #home a {
355 #header #header-inner #home a {
356 height: 40px;
356 height: 40px;
357 width: 46px;
357 width: 46px;
358 display: block;
358 display: block;
359 background: url("../images/button_home.png");
359 background: url("../images/button_home.png");
360 background-position: 0 0;
360 background-position: 0 0;
361 margin: 0;
361 margin: 0;
362 padding: 0;
362 padding: 0;
363 }
363 }
364
364
365 #header #header-inner #home a:hover {
365 #header #header-inner #home a:hover {
366 background-position: 0 -40px;
366 background-position: 0 -40px;
367 }
367 }
368
368
369 #header #header-inner #logo {
369 #header #header-inner #logo {
370 float: left;
370 float: left;
371 position: absolute;
371 position: absolute;
372 }
372 }
373
373
374 #header #header-inner #logo h1 {
374 #header #header-inner #logo h1 {
375 color: #FFF;
375 color: #FFF;
376 font-size: 20px;
376 font-size: 20px;
377 margin: 12px 0 0 13px;
377 margin: 12px 0 0 13px;
378 padding: 0;
378 padding: 0;
379 }
379 }
380
380
381 #header #header-inner #logo a {
381 #header #header-inner #logo a {
382 color: #fff;
382 color: #fff;
383 text-decoration: none;
383 text-decoration: none;
384 }
384 }
385
385
386 #header #header-inner #logo a:hover {
386 #header #header-inner #logo a:hover {
387 color: #bfe3ff;
387 color: #bfe3ff;
388 }
388 }
389
389
390 #header #header-inner #quick,#header #header-inner #quick ul {
390 #header #header-inner #quick,#header #header-inner #quick ul {
391 position: relative;
391 position: relative;
392 float: right;
392 float: right;
393 list-style-type: none;
393 list-style-type: none;
394 list-style-position: outside;
394 list-style-position: outside;
395 margin: 8px 8px 0 0;
395 margin: 8px 8px 0 0;
396 padding: 0;
396 padding: 0;
397 }
397 }
398
398
399 #header #header-inner #quick li {
399 #header #header-inner #quick li {
400 position: relative;
400 position: relative;
401 float: left;
401 float: left;
402 margin: 0 5px 0 0;
402 margin: 0 5px 0 0;
403 padding: 0;
403 padding: 0;
404 }
404 }
405
405
406 #header #header-inner #quick li a.menu_link {
406 #header #header-inner #quick li a.menu_link {
407 top: 0;
407 top: 0;
408 left: 0;
408 left: 0;
409 height: 1%;
409 height: 1%;
410 display: block;
410 display: block;
411 clear: both;
411 clear: both;
412 overflow: hidden;
412 overflow: hidden;
413 color: #FFF;
413 color: #FFF;
414 font-weight: 700;
414 font-weight: 700;
415 text-decoration: none;
415 text-decoration: none;
416 background: #369;
416 background: #369;
417 padding: 0;
417 padding: 0;
418 -webkit-border-radius: 4px 4px 4px 4px;
418 -webkit-border-radius: 4px 4px 4px 4px;
419 -khtml-border-radius: 4px 4px 4px 4px;
419 -khtml-border-radius: 4px 4px 4px 4px;
420 -moz-border-radius: 4px 4px 4px 4px;
420 -moz-border-radius: 4px 4px 4px 4px;
421 border-radius: 4px 4px 4px 4px;
421 border-radius: 4px 4px 4px 4px;
422 }
422 }
423
423
424 #header #header-inner #quick li span.short {
424 #header #header-inner #quick li span.short {
425 padding: 9px 6px 8px 6px;
425 padding: 9px 6px 8px 6px;
426 }
426 }
427
427
428 #header #header-inner #quick li span {
428 #header #header-inner #quick li span {
429 top: 0;
429 top: 0;
430 right: 0;
430 right: 0;
431 height: 1%;
431 height: 1%;
432 display: block;
432 display: block;
433 float: left;
433 float: left;
434 border-left: 1px solid #3f6f9f;
434 border-left: 1px solid #3f6f9f;
435 margin: 0;
435 margin: 0;
436 padding: 10px 12px 8px 10px;
436 padding: 10px 12px 8px 10px;
437 }
437 }
438
438
439 #header #header-inner #quick li span.normal {
439 #header #header-inner #quick li span.normal {
440 border: none;
440 border: none;
441 padding: 10px 12px 8px;
441 padding: 10px 12px 8px;
442 }
442 }
443
443
444 #header #header-inner #quick li span.icon {
444 #header #header-inner #quick li span.icon {
445 top: 0;
445 top: 0;
446 left: 0;
446 left: 0;
447 border-left: none;
447 border-left: none;
448 border-right: 1px solid #2e5c89;
448 border-right: 1px solid #2e5c89;
449 padding: 8px 6px 4px;
449 padding: 8px 6px 4px;
450 }
450 }
451
451
452 #header #header-inner #quick li span.icon_short {
452 #header #header-inner #quick li span.icon_short {
453 top: 0;
453 top: 0;
454 left: 0;
454 left: 0;
455 border-left: none;
455 border-left: none;
456 border-right: 1px solid #2e5c89;
456 border-right: 1px solid #2e5c89;
457 padding: 8px 6px 4px;
457 padding: 8px 6px 4px;
458 }
458 }
459
459
460 #header #header-inner #quick li span.icon img,#header #header-inner #quick li span.icon_short img
460 #header #header-inner #quick li span.icon img,#header #header-inner #quick li span.icon_short img
461 {
461 {
462 margin: 0px -2px 0px 0px;
462 margin: 0px -2px 0px 0px;
463 }
463 }
464
464
465 #header #header-inner #quick li a:hover {
465 #header #header-inner #quick li a:hover {
466 background: #4e4e4e no-repeat top left;
466 background: #4e4e4e no-repeat top left;
467 }
467 }
468
468
469 #header #header-inner #quick li a:hover span {
469 #header #header-inner #quick li a:hover span {
470 border-left: 1px solid #545454;
470 border-left: 1px solid #545454;
471 }
471 }
472
472
473 #header #header-inner #quick li a:hover span.icon,#header #header-inner #quick li a:hover span.icon_short
473 #header #header-inner #quick li a:hover span.icon,#header #header-inner #quick li a:hover span.icon_short
474 {
474 {
475 border-left: none;
475 border-left: none;
476 border-right: 1px solid #464646;
476 border-right: 1px solid #464646;
477 }
477 }
478
478
479 #header #header-inner #quick ul {
479 #header #header-inner #quick ul {
480 top: 29px;
480 top: 29px;
481 right: 0;
481 right: 0;
482 min-width: 200px;
482 min-width: 200px;
483 display: none;
483 display: none;
484 position: absolute;
484 position: absolute;
485 background: #FFF;
485 background: #FFF;
486 border: 1px solid #666;
486 border: 1px solid #666;
487 border-top: 1px solid #003367;
487 border-top: 1px solid #003367;
488 z-index: 100;
488 z-index: 100;
489 margin: 0px 0px 0px 0px;
489 margin: 0px 0px 0px 0px;
490 padding: 0;
490 padding: 0;
491 }
491 }
492
492
493 #header #header-inner #quick ul.repo_switcher {
493 #header #header-inner #quick ul.repo_switcher {
494 max-height: 275px;
494 max-height: 275px;
495 overflow-x: hidden;
495 overflow-x: hidden;
496 overflow-y: auto;
496 overflow-y: auto;
497 }
497 }
498
498
499 #header #header-inner #quick ul.repo_switcher li.qfilter_rs {
499 #header #header-inner #quick ul.repo_switcher li.qfilter_rs {
500 float: none;
500 float: none;
501 margin: 0;
501 margin: 0;
502 border-bottom: 2px solid #003367;
502 border-bottom: 2px solid #003367;
503 }
503 }
504
504
505 #header #header-inner #quick .repo_switcher_type {
505 #header #header-inner #quick .repo_switcher_type {
506 position: absolute;
506 position: absolute;
507 left: 0;
507 left: 0;
508 top: 9px;
508 top: 9px;
509 }
509 }
510
510
511 #header #header-inner #quick li ul li {
511 #header #header-inner #quick li ul li {
512 border-bottom: 1px solid #ddd;
512 border-bottom: 1px solid #ddd;
513 }
513 }
514
514
515 #header #header-inner #quick li ul li a {
515 #header #header-inner #quick li ul li a {
516 width: 182px;
516 width: 182px;
517 height: auto;
517 height: auto;
518 display: block;
518 display: block;
519 float: left;
519 float: left;
520 background: #FFF;
520 background: #FFF;
521 color: #003367;
521 color: #003367;
522 font-weight: 400;
522 font-weight: 400;
523 margin: 0;
523 margin: 0;
524 padding: 7px 9px;
524 padding: 7px 9px;
525 }
525 }
526
526
527 #header #header-inner #quick li ul li a:hover {
527 #header #header-inner #quick li ul li a:hover {
528 color: #000;
528 color: #000;
529 background: #FFF;
529 background: #FFF;
530 }
530 }
531
531
532 #header #header-inner #quick ul ul {
532 #header #header-inner #quick ul ul {
533 top: auto;
533 top: auto;
534 }
534 }
535
535
536 #header #header-inner #quick li ul ul {
536 #header #header-inner #quick li ul ul {
537 right: 200px;
537 right: 200px;
538 max-height: 275px;
538 max-height: 275px;
539 overflow: auto;
539 overflow: auto;
540 overflow-x: hidden;
540 overflow-x: hidden;
541 white-space: normal;
541 white-space: normal;
542 }
542 }
543
543
544 #header #header-inner #quick li ul li a.journal,#header #header-inner #quick li ul li a.journal:hover
544 #header #header-inner #quick li ul li a.journal,#header #header-inner #quick li ul li a.journal:hover
545 {
545 {
546 background: url("../images/icons/book.png") no-repeat scroll 4px 9px
546 background: url("../images/icons/book.png") no-repeat scroll 4px 9px
547 #FFF;
547 #FFF;
548 width: 167px;
548 width: 167px;
549 margin: 0;
549 margin: 0;
550 padding: 12px 9px 7px 24px;
550 padding: 12px 9px 7px 24px;
551 }
551 }
552
552
553 #header #header-inner #quick li ul li a.private_repo,#header #header-inner #quick li ul li a.private_repo:hover
553 #header #header-inner #quick li ul li a.private_repo,#header #header-inner #quick li ul li a.private_repo:hover
554 {
554 {
555 background: url("../images/icons/lock.png") no-repeat scroll 4px 9px
555 background: url("../images/icons/lock.png") no-repeat scroll 4px 9px
556 #FFF;
556 #FFF;
557 min-width: 167px;
557 min-width: 167px;
558 margin: 0;
558 margin: 0;
559 padding: 12px 9px 7px 24px;
559 padding: 12px 9px 7px 24px;
560 }
560 }
561
561
562 #header #header-inner #quick li ul li a.public_repo,#header #header-inner #quick li ul li a.public_repo:hover
562 #header #header-inner #quick li ul li a.public_repo,#header #header-inner #quick li ul li a.public_repo:hover
563 {
563 {
564 background: url("../images/icons/lock_open.png") no-repeat scroll 4px
564 background: url("../images/icons/lock_open.png") no-repeat scroll 4px
565 9px #FFF;
565 9px #FFF;
566 min-width: 167px;
566 min-width: 167px;
567 margin: 0;
567 margin: 0;
568 padding: 12px 9px 7px 24px;
568 padding: 12px 9px 7px 24px;
569 }
569 }
570
570
571 #header #header-inner #quick li ul li a.hg,#header #header-inner #quick li ul li a.hg:hover
571 #header #header-inner #quick li ul li a.hg,#header #header-inner #quick li ul li a.hg:hover
572 {
572 {
573 background: url("../images/icons/hgicon.png") no-repeat scroll 4px 9px
573 background: url("../images/icons/hgicon.png") no-repeat scroll 4px 9px
574 #FFF;
574 #FFF;
575 min-width: 167px;
575 min-width: 167px;
576 margin: 0 0 0 14px;
576 margin: 0 0 0 14px;
577 padding: 12px 9px 7px 24px;
577 padding: 12px 9px 7px 24px;
578 }
578 }
579
579
580 #header #header-inner #quick li ul li a.git,#header #header-inner #quick li ul li a.git:hover
580 #header #header-inner #quick li ul li a.git,#header #header-inner #quick li ul li a.git:hover
581 {
581 {
582 background: url("../images/icons/giticon.png") no-repeat scroll 4px 9px
582 background: url("../images/icons/giticon.png") no-repeat scroll 4px 9px
583 #FFF;
583 #FFF;
584 min-width: 167px;
584 min-width: 167px;
585 margin: 0 0 0 14px;
585 margin: 0 0 0 14px;
586 padding: 12px 9px 7px 24px;
586 padding: 12px 9px 7px 24px;
587 }
587 }
588
588
589 #header #header-inner #quick li ul li a.repos,#header #header-inner #quick li ul li a.repos:hover
589 #header #header-inner #quick li ul li a.repos,#header #header-inner #quick li ul li a.repos:hover
590 {
590 {
591 background: url("../images/icons/database_edit.png") no-repeat scroll
591 background: url("../images/icons/database_edit.png") no-repeat scroll
592 4px 9px #FFF;
592 4px 9px #FFF;
593 width: 167px;
593 width: 167px;
594 margin: 0;
594 margin: 0;
595 padding: 12px 9px 7px 24px;
595 padding: 12px 9px 7px 24px;
596 }
596 }
597
597
598 #header #header-inner #quick li ul li a.repos_groups,#header #header-inner #quick li ul li a.repos_groups:hover
598 #header #header-inner #quick li ul li a.repos_groups,#header #header-inner #quick li ul li a.repos_groups:hover
599 {
599 {
600 background: url("../images/icons/database_link.png") no-repeat scroll
600 background: url("../images/icons/database_link.png") no-repeat scroll
601 4px 9px #FFF;
601 4px 9px #FFF;
602 width: 167px;
602 width: 167px;
603 margin: 0;
603 margin: 0;
604 padding: 12px 9px 7px 24px;
604 padding: 12px 9px 7px 24px;
605 }
605 }
606
606
607 #header #header-inner #quick li ul li a.users,#header #header-inner #quick li ul li a.users:hover
607 #header #header-inner #quick li ul li a.users,#header #header-inner #quick li ul li a.users:hover
608 {
608 {
609 background: #FFF url("../images/icons/user_edit.png") no-repeat 4px 9px;
609 background: #FFF url("../images/icons/user_edit.png") no-repeat 4px 9px;
610 width: 167px;
610 width: 167px;
611 margin: 0;
611 margin: 0;
612 padding: 12px 9px 7px 24px;
612 padding: 12px 9px 7px 24px;
613 }
613 }
614
614
615 #header #header-inner #quick li ul li a.groups,#header #header-inner #quick li ul li a.groups:hover
615 #header #header-inner #quick li ul li a.groups,#header #header-inner #quick li ul li a.groups:hover
616 {
616 {
617 background: #FFF url("../images/icons/group_edit.png") no-repeat 4px 9px;
617 background: #FFF url("../images/icons/group_edit.png") no-repeat 4px 9px;
618 width: 167px;
618 width: 167px;
619 margin: 0;
619 margin: 0;
620 padding: 12px 9px 7px 24px;
620 padding: 12px 9px 7px 24px;
621 }
621 }
622
622
623 #header #header-inner #quick li ul li a.settings,#header #header-inner #quick li ul li a.settings:hover
623 #header #header-inner #quick li ul li a.settings,#header #header-inner #quick li ul li a.settings:hover
624 {
624 {
625 background: #FFF url("../images/icons/cog.png") no-repeat 4px 9px;
625 background: #FFF url("../images/icons/cog.png") no-repeat 4px 9px;
626 width: 167px;
626 width: 167px;
627 margin: 0;
627 margin: 0;
628 padding: 12px 9px 7px 24px;
628 padding: 12px 9px 7px 24px;
629 }
629 }
630
630
631 #header #header-inner #quick li ul li a.permissions,#header #header-inner #quick li ul li a.permissions:hover
631 #header #header-inner #quick li ul li a.permissions,#header #header-inner #quick li ul li a.permissions:hover
632 {
632 {
633 background: #FFF url("../images/icons/key.png") no-repeat 4px 9px;
633 background: #FFF url("../images/icons/key.png") no-repeat 4px 9px;
634 width: 167px;
634 width: 167px;
635 margin: 0;
635 margin: 0;
636 padding: 12px 9px 7px 24px;
636 padding: 12px 9px 7px 24px;
637 }
637 }
638
638
639 #header #header-inner #quick li ul li a.ldap,#header #header-inner #quick li ul li a.ldap:hover
639 #header #header-inner #quick li ul li a.ldap,#header #header-inner #quick li ul li a.ldap:hover
640 {
640 {
641 background: #FFF url("../images/icons/server_key.png") no-repeat 4px 9px;
641 background: #FFF url("../images/icons/server_key.png") no-repeat 4px 9px;
642 width: 167px;
642 width: 167px;
643 margin: 0;
643 margin: 0;
644 padding: 12px 9px 7px 24px;
644 padding: 12px 9px 7px 24px;
645 }
645 }
646
646
647 #header #header-inner #quick li ul li a.fork,#header #header-inner #quick li ul li a.fork:hover
647 #header #header-inner #quick li ul li a.fork,#header #header-inner #quick li ul li a.fork:hover
648 {
648 {
649 background: #FFF url("../images/icons/arrow_divide.png") no-repeat 4px
649 background: #FFF url("../images/icons/arrow_divide.png") no-repeat 4px
650 9px;
650 9px;
651 width: 167px;
651 width: 167px;
652 margin: 0;
652 margin: 0;
653 padding: 12px 9px 7px 24px;
653 padding: 12px 9px 7px 24px;
654 }
654 }
655
655
656 #header #header-inner #quick li ul li a.pull_request,#header #header-inner #quick li ul li a.pull_request:hover
656 #header #header-inner #quick li ul li a.pull_request,#header #header-inner #quick li ul li a.pull_request:hover
657 {
657 {
658 background: #FFF url("../images/icons/arrow_join.png") no-repeat 4px
658 background: #FFF url("../images/icons/arrow_join.png") no-repeat 4px
659 9px;
659 9px;
660 width: 167px;
660 width: 167px;
661 margin: 0;
661 margin: 0;
662 padding: 12px 9px 7px 24px;
662 padding: 12px 9px 7px 24px;
663 }
663 }
664
664
665 #header #header-inner #quick li ul li a.search,#header #header-inner #quick li ul li a.search:hover
665 #header #header-inner #quick li ul li a.search,#header #header-inner #quick li ul li a.search:hover
666 {
666 {
667 background: #FFF url("../images/icons/search_16.png") no-repeat 4px 9px;
667 background: #FFF url("../images/icons/search_16.png") no-repeat 4px 9px;
668 width: 167px;
668 width: 167px;
669 margin: 0;
669 margin: 0;
670 padding: 12px 9px 7px 24px;
670 padding: 12px 9px 7px 24px;
671 }
671 }
672
672
673 #header #header-inner #quick li ul li a.delete,#header #header-inner #quick li ul li a.delete:hover
673 #header #header-inner #quick li ul li a.delete,#header #header-inner #quick li ul li a.delete:hover
674 {
674 {
675 background: #FFF url("../images/icons/delete.png") no-repeat 4px 9px;
675 background: #FFF url("../images/icons/delete.png") no-repeat 4px 9px;
676 width: 167px;
676 width: 167px;
677 margin: 0;
677 margin: 0;
678 padding: 12px 9px 7px 24px;
678 padding: 12px 9px 7px 24px;
679 }
679 }
680
680
681 #header #header-inner #quick li ul li a.branches,#header #header-inner #quick li ul li a.branches:hover
681 #header #header-inner #quick li ul li a.branches,#header #header-inner #quick li ul li a.branches:hover
682 {
682 {
683 background: #FFF url("../images/icons/arrow_branch.png") no-repeat 4px
683 background: #FFF url("../images/icons/arrow_branch.png") no-repeat 4px
684 9px;
684 9px;
685 width: 167px;
685 width: 167px;
686 margin: 0;
686 margin: 0;
687 padding: 12px 9px 7px 24px;
687 padding: 12px 9px 7px 24px;
688 }
688 }
689
689
690 #header #header-inner #quick li ul li a.tags,
690 #header #header-inner #quick li ul li a.tags,
691 #header #header-inner #quick li ul li a.tags:hover{
691 #header #header-inner #quick li ul li a.tags:hover{
692 background: #FFF url("../images/icons/tag_blue.png") no-repeat 4px 9px;
692 background: #FFF url("../images/icons/tag_blue.png") no-repeat 4px 9px;
693 width: 167px;
693 width: 167px;
694 margin: 0;
694 margin: 0;
695 padding: 12px 9px 7px 24px;
695 padding: 12px 9px 7px 24px;
696 }
696 }
697
697
698 #header #header-inner #quick li ul li a.bookmarks,
698 #header #header-inner #quick li ul li a.bookmarks,
699 #header #header-inner #quick li ul li a.bookmarks:hover{
699 #header #header-inner #quick li ul li a.bookmarks:hover{
700 background: #FFF url("../images/icons/tag_green.png") no-repeat 4px 9px;
700 background: #FFF url("../images/icons/tag_green.png") no-repeat 4px 9px;
701 width: 167px;
701 width: 167px;
702 margin: 0;
702 margin: 0;
703 padding: 12px 9px 7px 24px;
703 padding: 12px 9px 7px 24px;
704 }
704 }
705
705
706 #header #header-inner #quick li ul li a.admin,
706 #header #header-inner #quick li ul li a.admin,
707 #header #header-inner #quick li ul li a.admin:hover{
707 #header #header-inner #quick li ul li a.admin:hover{
708 background: #FFF url("../images/icons/cog_edit.png") no-repeat 4px 9px;
708 background: #FFF url("../images/icons/cog_edit.png") no-repeat 4px 9px;
709 width: 167px;
709 width: 167px;
710 margin: 0;
710 margin: 0;
711 padding: 12px 9px 7px 24px;
711 padding: 12px 9px 7px 24px;
712 }
712 }
713
713
714 .groups_breadcrumbs a {
714 .groups_breadcrumbs a {
715 color: #fff;
715 color: #fff;
716 }
716 }
717
717
718 .groups_breadcrumbs a:hover {
718 .groups_breadcrumbs a:hover {
719 color: #bfe3ff;
719 color: #bfe3ff;
720 text-decoration: none;
720 text-decoration: none;
721 }
721 }
722
722
723 td.quick_repo_menu {
723 td.quick_repo_menu {
724 background: #FFF url("../images/vertical-indicator.png") 8px 50% no-repeat !important;
724 background: #FFF url("../images/vertical-indicator.png") 8px 50% no-repeat !important;
725 cursor: pointer;
725 cursor: pointer;
726 width: 8px;
726 width: 8px;
727 border: 1px solid transparent;
727 border: 1px solid transparent;
728 }
728 }
729
729
730 td.quick_repo_menu.active {
730 td.quick_repo_menu.active {
731 background: url("../images/dt-arrow-dn.png") no-repeat scroll 5px 50% #FFFFFF !important;
731 background: url("../images/dt-arrow-dn.png") no-repeat scroll 5px 50% #FFFFFF !important;
732 border: 1px solid #003367;
732 border: 1px solid #003367;
733 box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
733 box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
734 cursor: pointer;
734 cursor: pointer;
735 }
735 }
736
736
737 td.quick_repo_menu .menu_items {
737 td.quick_repo_menu .menu_items {
738 margin-top: 10px;
738 margin-top: 10px;
739 margin-left:-6px;
739 margin-left:-6px;
740 width: 150px;
740 width: 150px;
741 position: absolute;
741 position: absolute;
742 background-color: #FFF;
742 background-color: #FFF;
743 background: none repeat scroll 0 0 #FFFFFF;
743 background: none repeat scroll 0 0 #FFFFFF;
744 border-color: #003367 #666666 #666666;
744 border-color: #003367 #666666 #666666;
745 border-right: 1px solid #666666;
745 border-right: 1px solid #666666;
746 border-style: solid;
746 border-style: solid;
747 border-width: 1px;
747 border-width: 1px;
748 box-shadow: 2px 8px 4px rgba(0, 0, 0, 0.2);
748 box-shadow: 2px 8px 4px rgba(0, 0, 0, 0.2);
749 border-top-style: none;
749 border-top-style: none;
750 }
750 }
751
751
752 td.quick_repo_menu .menu_items li {
752 td.quick_repo_menu .menu_items li {
753 padding: 0 !important;
753 padding: 0 !important;
754 }
754 }
755
755
756 td.quick_repo_menu .menu_items a {
756 td.quick_repo_menu .menu_items a {
757 display: block;
757 display: block;
758 padding: 4px 12px 4px 8px;
758 padding: 4px 12px 4px 8px;
759 }
759 }
760
760
761 td.quick_repo_menu .menu_items a:hover {
761 td.quick_repo_menu .menu_items a:hover {
762 background-color: #EEE;
762 background-color: #EEE;
763 text-decoration: none;
763 text-decoration: none;
764 }
764 }
765
765
766 td.quick_repo_menu .menu_items .icon img {
766 td.quick_repo_menu .menu_items .icon img {
767 margin-bottom: -2px;
767 margin-bottom: -2px;
768 }
768 }
769
769
770 td.quick_repo_menu .menu_items.hidden {
770 td.quick_repo_menu .menu_items.hidden {
771 display: none;
771 display: none;
772 }
772 }
773
773
774 .yui-dt-first th {
774 .yui-dt-first th {
775 text-align: left;
775 text-align: left;
776 }
776 }
777
777
778 /*
778 /*
779 Copyright (c) 2011, Yahoo! Inc. All rights reserved.
779 Copyright (c) 2011, Yahoo! Inc. All rights reserved.
780 Code licensed under the BSD License:
780 Code licensed under the BSD License:
781 http://developer.yahoo.com/yui/license.html
781 http://developer.yahoo.com/yui/license.html
782 version: 2.9.0
782 version: 2.9.0
783 */
783 */
784 .yui-skin-sam .yui-dt-mask {
784 .yui-skin-sam .yui-dt-mask {
785 position: absolute;
785 position: absolute;
786 z-index: 9500;
786 z-index: 9500;
787 }
787 }
788 .yui-dt-tmp {
788 .yui-dt-tmp {
789 position: absolute;
789 position: absolute;
790 left: -9000px;
790 left: -9000px;
791 }
791 }
792 .yui-dt-scrollable .yui-dt-bd { overflow: auto }
792 .yui-dt-scrollable .yui-dt-bd { overflow: auto }
793 .yui-dt-scrollable .yui-dt-hd {
793 .yui-dt-scrollable .yui-dt-hd {
794 overflow: hidden;
794 overflow: hidden;
795 position: relative;
795 position: relative;
796 }
796 }
797 .yui-dt-scrollable .yui-dt-bd thead tr,
797 .yui-dt-scrollable .yui-dt-bd thead tr,
798 .yui-dt-scrollable .yui-dt-bd thead th {
798 .yui-dt-scrollable .yui-dt-bd thead th {
799 position: absolute;
799 position: absolute;
800 left: -1500px;
800 left: -1500px;
801 }
801 }
802 .yui-dt-scrollable tbody { -moz-outline: 0 }
802 .yui-dt-scrollable tbody { -moz-outline: 0 }
803 .yui-skin-sam thead .yui-dt-sortable { cursor: pointer }
803 .yui-skin-sam thead .yui-dt-sortable { cursor: pointer }
804 .yui-skin-sam thead .yui-dt-draggable { cursor: move }
804 .yui-skin-sam thead .yui-dt-draggable { cursor: move }
805 .yui-dt-coltarget {
805 .yui-dt-coltarget {
806 position: absolute;
806 position: absolute;
807 z-index: 999;
807 z-index: 999;
808 }
808 }
809 .yui-dt-hd { zoom: 1 }
809 .yui-dt-hd { zoom: 1 }
810 th.yui-dt-resizeable .yui-dt-resizerliner { position: relative }
810 th.yui-dt-resizeable .yui-dt-resizerliner { position: relative }
811 .yui-dt-resizer {
811 .yui-dt-resizer {
812 position: absolute;
812 position: absolute;
813 right: 0;
813 right: 0;
814 bottom: 0;
814 bottom: 0;
815 height: 100%;
815 height: 100%;
816 cursor: e-resize;
816 cursor: e-resize;
817 cursor: col-resize;
817 cursor: col-resize;
818 background-color: #CCC;
818 background-color: #CCC;
819 opacity: 0;
819 opacity: 0;
820 filter: alpha(opacity=0);
820 filter: alpha(opacity=0);
821 }
821 }
822 .yui-dt-resizerproxy {
822 .yui-dt-resizerproxy {
823 visibility: hidden;
823 visibility: hidden;
824 position: absolute;
824 position: absolute;
825 z-index: 9000;
825 z-index: 9000;
826 background-color: #CCC;
826 background-color: #CCC;
827 opacity: 0;
827 opacity: 0;
828 filter: alpha(opacity=0);
828 filter: alpha(opacity=0);
829 }
829 }
830 th.yui-dt-hidden .yui-dt-liner,
830 th.yui-dt-hidden .yui-dt-liner,
831 td.yui-dt-hidden .yui-dt-liner,
831 td.yui-dt-hidden .yui-dt-liner,
832 th.yui-dt-hidden .yui-dt-resizer { display: none }
832 th.yui-dt-hidden .yui-dt-resizer { display: none }
833 .yui-dt-editor,
833 .yui-dt-editor,
834 .yui-dt-editor-shim {
834 .yui-dt-editor-shim {
835 position: absolute;
835 position: absolute;
836 z-index: 9000;
836 z-index: 9000;
837 }
837 }
838 .yui-skin-sam .yui-dt table {
838 .yui-skin-sam .yui-dt table {
839 margin: 0;
839 margin: 0;
840 padding: 0;
840 padding: 0;
841 font-family: arial;
841 font-family: arial;
842 font-size: inherit;
842 font-size: inherit;
843 border-collapse: separate;
843 border-collapse: separate;
844 *border-collapse: collapse;
844 *border-collapse: collapse;
845 border-spacing: 0;
845 border-spacing: 0;
846 border: 1px solid #7f7f7f;
846 border: 1px solid #7f7f7f;
847 }
847 }
848 .yui-skin-sam .yui-dt thead { border-spacing: 0 }
848 .yui-skin-sam .yui-dt thead { border-spacing: 0 }
849 .yui-skin-sam .yui-dt caption {
849 .yui-skin-sam .yui-dt caption {
850 color: #000;
850 color: #000;
851 font-size: 85%;
851 font-size: 85%;
852 font-weight: normal;
852 font-weight: normal;
853 font-style: italic;
853 font-style: italic;
854 line-height: 1;
854 line-height: 1;
855 padding: 1em 0;
855 padding: 1em 0;
856 text-align: center;
856 text-align: center;
857 }
857 }
858 .yui-skin-sam .yui-dt th { background: #d8d8da url(../images/sprite.png) repeat-x 0 0 }
858 .yui-skin-sam .yui-dt th { background: #d8d8da url(../images/sprite.png) repeat-x 0 0 }
859 .yui-skin-sam .yui-dt th,
859 .yui-skin-sam .yui-dt th,
860 .yui-skin-sam .yui-dt th a {
860 .yui-skin-sam .yui-dt th a {
861 font-weight: normal;
861 font-weight: normal;
862 text-decoration: none;
862 text-decoration: none;
863 color: #000;
863 color: #000;
864 vertical-align: bottom;
864 vertical-align: bottom;
865 }
865 }
866 .yui-skin-sam .yui-dt th {
866 .yui-skin-sam .yui-dt th {
867 margin: 0;
867 margin: 0;
868 padding: 0;
868 padding: 0;
869 border: 0;
869 border: 0;
870 border-right: 1px solid #cbcbcb;
870 border-right: 1px solid #cbcbcb;
871 }
871 }
872 .yui-skin-sam .yui-dt tr.yui-dt-first td { border-top: 1px solid #7f7f7f }
872 .yui-skin-sam .yui-dt tr.yui-dt-first td { border-top: 1px solid #7f7f7f }
873 .yui-skin-sam .yui-dt th .yui-dt-liner { white-space: nowrap }
873 .yui-skin-sam .yui-dt th .yui-dt-liner { white-space: nowrap }
874 .yui-skin-sam .yui-dt-liner {
874 .yui-skin-sam .yui-dt-liner {
875 margin: 0;
875 margin: 0;
876 padding: 0;
876 padding: 0;
877 }
877 }
878 .yui-skin-sam .yui-dt-coltarget {
878 .yui-skin-sam .yui-dt-coltarget {
879 width: 5px;
879 width: 5px;
880 background-color: red;
880 background-color: red;
881 }
881 }
882 .yui-skin-sam .yui-dt td {
882 .yui-skin-sam .yui-dt td {
883 margin: 0;
883 margin: 0;
884 padding: 0;
884 padding: 0;
885 border: 0;
885 border: 0;
886 border-right: 1px solid #cbcbcb;
886 border-right: 1px solid #cbcbcb;
887 text-align: left;
887 text-align: left;
888 }
888 }
889 .yui-skin-sam .yui-dt-list td { border-right: 0 }
889 .yui-skin-sam .yui-dt-list td { border-right: 0 }
890 .yui-skin-sam .yui-dt-resizer { width: 6px }
890 .yui-skin-sam .yui-dt-resizer { width: 6px }
891 .yui-skin-sam .yui-dt-mask {
891 .yui-skin-sam .yui-dt-mask {
892 background-color: #000;
892 background-color: #000;
893 opacity: .25;
893 opacity: .25;
894 filter: alpha(opacity=25);
894 filter: alpha(opacity=25);
895 }
895 }
896 .yui-skin-sam .yui-dt-message { background-color: #FFF }
896 .yui-skin-sam .yui-dt-message { background-color: #FFF }
897 .yui-skin-sam .yui-dt-scrollable table { border: 0 }
897 .yui-skin-sam .yui-dt-scrollable table { border: 0 }
898 .yui-skin-sam .yui-dt-scrollable .yui-dt-hd {
898 .yui-skin-sam .yui-dt-scrollable .yui-dt-hd {
899 border-left: 1px solid #7f7f7f;
899 border-left: 1px solid #7f7f7f;
900 border-top: 1px solid #7f7f7f;
900 border-top: 1px solid #7f7f7f;
901 border-right: 1px solid #7f7f7f;
901 border-right: 1px solid #7f7f7f;
902 }
902 }
903 .yui-skin-sam .yui-dt-scrollable .yui-dt-bd {
903 .yui-skin-sam .yui-dt-scrollable .yui-dt-bd {
904 border-left: 1px solid #7f7f7f;
904 border-left: 1px solid #7f7f7f;
905 border-bottom: 1px solid #7f7f7f;
905 border-bottom: 1px solid #7f7f7f;
906 border-right: 1px solid #7f7f7f;
906 border-right: 1px solid #7f7f7f;
907 background-color: #FFF;
907 background-color: #FFF;
908 }
908 }
909 .yui-skin-sam .yui-dt-scrollable .yui-dt-data tr.yui-dt-last td { border-bottom: 1px solid #7f7f7f }
909 .yui-skin-sam .yui-dt-scrollable .yui-dt-data tr.yui-dt-last td { border-bottom: 1px solid #7f7f7f }
910 .yui-skin-sam th.yui-dt-asc,
910 .yui-skin-sam th.yui-dt-asc,
911 .yui-skin-sam th.yui-dt-desc { background: url(../images/sprite.png) repeat-x 0 -100px }
911 .yui-skin-sam th.yui-dt-desc { background: url(../images/sprite.png) repeat-x 0 -100px }
912 .yui-skin-sam th.yui-dt-sortable .yui-dt-label { margin-right: 10px }
912 .yui-skin-sam th.yui-dt-sortable .yui-dt-label { margin-right: 10px }
913 .yui-skin-sam th.yui-dt-asc .yui-dt-liner { background: url(../images/dt-arrow-up.png) no-repeat right }
913 .yui-skin-sam th.yui-dt-asc .yui-dt-liner { background: url(../images/dt-arrow-up.png) no-repeat right }
914 .yui-skin-sam th.yui-dt-desc .yui-dt-liner { background: url(../images/dt-arrow-dn.png) no-repeat right }
914 .yui-skin-sam th.yui-dt-desc .yui-dt-liner { background: url(../images/dt-arrow-dn.png) no-repeat right }
915 tbody .yui-dt-editable { cursor: pointer }
915 tbody .yui-dt-editable { cursor: pointer }
916 .yui-dt-editor {
916 .yui-dt-editor {
917 text-align: left;
917 text-align: left;
918 background-color: #f2f2f2;
918 background-color: #f2f2f2;
919 border: 1px solid #808080;
919 border: 1px solid #808080;
920 padding: 6px;
920 padding: 6px;
921 }
921 }
922 .yui-dt-editor label {
922 .yui-dt-editor label {
923 padding-left: 4px;
923 padding-left: 4px;
924 padding-right: 6px;
924 padding-right: 6px;
925 }
925 }
926 .yui-dt-editor .yui-dt-button {
926 .yui-dt-editor .yui-dt-button {
927 padding-top: 6px;
927 padding-top: 6px;
928 text-align: right;
928 text-align: right;
929 }
929 }
930 .yui-dt-editor .yui-dt-button button {
930 .yui-dt-editor .yui-dt-button button {
931 background: url(../images/sprite.png) repeat-x 0 0;
931 background: url(../images/sprite.png) repeat-x 0 0;
932 border: 1px solid #999;
932 border: 1px solid #999;
933 width: 4em;
933 width: 4em;
934 height: 1.8em;
934 height: 1.8em;
935 margin-left: 6px;
935 margin-left: 6px;
936 }
936 }
937 .yui-dt-editor .yui-dt-button button.yui-dt-default {
937 .yui-dt-editor .yui-dt-button button.yui-dt-default {
938 background: url(../images/sprite.png) repeat-x 0 -1400px;
938 background: url(../images/sprite.png) repeat-x 0 -1400px;
939 background-color: #5584e0;
939 background-color: #5584e0;
940 border: 1px solid #304369;
940 border: 1px solid #304369;
941 color: #FFF;
941 color: #FFF;
942 }
942 }
943 .yui-dt-editor .yui-dt-button button:hover {
943 .yui-dt-editor .yui-dt-button button:hover {
944 background: url(../images/sprite.png) repeat-x 0 -1300px;
944 background: url(../images/sprite.png) repeat-x 0 -1300px;
945 color: #000;
945 color: #000;
946 }
946 }
947 .yui-dt-editor .yui-dt-button button:active {
947 .yui-dt-editor .yui-dt-button button:active {
948 background: url(../images/sprite.png) repeat-x 0 -1700px;
948 background: url(../images/sprite.png) repeat-x 0 -1700px;
949 color: #000;
949 color: #000;
950 }
950 }
951 .yui-skin-sam tr.yui-dt-even { background-color: #FFF }
951 .yui-skin-sam tr.yui-dt-even { background-color: #FFF }
952 .yui-skin-sam tr.yui-dt-odd { background-color: #edf5ff }
952 .yui-skin-sam tr.yui-dt-odd { background-color: #edf5ff }
953 .yui-skin-sam tr.yui-dt-even td.yui-dt-asc,
953 .yui-skin-sam tr.yui-dt-even td.yui-dt-asc,
954 .yui-skin-sam tr.yui-dt-even td.yui-dt-desc { background-color: #edf5ff }
954 .yui-skin-sam tr.yui-dt-even td.yui-dt-desc { background-color: #edf5ff }
955 .yui-skin-sam tr.yui-dt-odd td.yui-dt-asc,
955 .yui-skin-sam tr.yui-dt-odd td.yui-dt-asc,
956 .yui-skin-sam tr.yui-dt-odd td.yui-dt-desc { background-color: #dbeaff }
956 .yui-skin-sam tr.yui-dt-odd td.yui-dt-desc { background-color: #dbeaff }
957 .yui-skin-sam .yui-dt-list tr.yui-dt-even { background-color: #FFF }
957 .yui-skin-sam .yui-dt-list tr.yui-dt-even { background-color: #FFF }
958 .yui-skin-sam .yui-dt-list tr.yui-dt-odd { background-color: #FFF }
958 .yui-skin-sam .yui-dt-list tr.yui-dt-odd { background-color: #FFF }
959 .yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-asc,
959 .yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-asc,
960 .yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-desc { background-color: #edf5ff }
960 .yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-desc { background-color: #edf5ff }
961 .yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-asc,
961 .yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-asc,
962 .yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-desc { background-color: #edf5ff }
962 .yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-desc { background-color: #edf5ff }
963 .yui-skin-sam th.yui-dt-highlighted,
963 .yui-skin-sam th.yui-dt-highlighted,
964 .yui-skin-sam th.yui-dt-highlighted a { background-color: #b2d2ff }
964 .yui-skin-sam th.yui-dt-highlighted a { background-color: #b2d2ff }
965 .yui-skin-sam tr.yui-dt-highlighted,
965 .yui-skin-sam tr.yui-dt-highlighted,
966 .yui-skin-sam tr.yui-dt-highlighted td.yui-dt-asc,
966 .yui-skin-sam tr.yui-dt-highlighted td.yui-dt-asc,
967 .yui-skin-sam tr.yui-dt-highlighted td.yui-dt-desc,
967 .yui-skin-sam tr.yui-dt-highlighted td.yui-dt-desc,
968 .yui-skin-sam tr.yui-dt-even td.yui-dt-highlighted,
968 .yui-skin-sam tr.yui-dt-even td.yui-dt-highlighted,
969 .yui-skin-sam tr.yui-dt-odd td.yui-dt-highlighted {
969 .yui-skin-sam tr.yui-dt-odd td.yui-dt-highlighted {
970 cursor: pointer;
970 cursor: pointer;
971 background-color: #b2d2ff;
971 background-color: #b2d2ff;
972 }
972 }
973 .yui-skin-sam .yui-dt-list th.yui-dt-highlighted,
973 .yui-skin-sam .yui-dt-list th.yui-dt-highlighted,
974 .yui-skin-sam .yui-dt-list th.yui-dt-highlighted a { background-color: #b2d2ff }
974 .yui-skin-sam .yui-dt-list th.yui-dt-highlighted a { background-color: #b2d2ff }
975 .yui-skin-sam .yui-dt-list tr.yui-dt-highlighted,
975 .yui-skin-sam .yui-dt-list tr.yui-dt-highlighted,
976 .yui-skin-sam .yui-dt-list tr.yui-dt-highlighted td.yui-dt-asc,
976 .yui-skin-sam .yui-dt-list tr.yui-dt-highlighted td.yui-dt-asc,
977 .yui-skin-sam .yui-dt-list tr.yui-dt-highlighted td.yui-dt-desc,
977 .yui-skin-sam .yui-dt-list tr.yui-dt-highlighted td.yui-dt-desc,
978 .yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-highlighted,
978 .yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-highlighted,
979 .yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-highlighted {
979 .yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-highlighted {
980 cursor: pointer;
980 cursor: pointer;
981 background-color: #b2d2ff;
981 background-color: #b2d2ff;
982 }
982 }
983 .yui-skin-sam th.yui-dt-selected,
983 .yui-skin-sam th.yui-dt-selected,
984 .yui-skin-sam th.yui-dt-selected a { background-color: #446cd7 }
984 .yui-skin-sam th.yui-dt-selected a { background-color: #446cd7 }
985 .yui-skin-sam tr.yui-dt-selected td,
985 .yui-skin-sam tr.yui-dt-selected td,
986 .yui-skin-sam tr.yui-dt-selected td.yui-dt-asc,
986 .yui-skin-sam tr.yui-dt-selected td.yui-dt-asc,
987 .yui-skin-sam tr.yui-dt-selected td.yui-dt-desc {
987 .yui-skin-sam tr.yui-dt-selected td.yui-dt-desc {
988 background-color: #426fd9;
988 background-color: #426fd9;
989 color: #FFF;
989 color: #FFF;
990 }
990 }
991 .yui-skin-sam tr.yui-dt-even td.yui-dt-selected,
991 .yui-skin-sam tr.yui-dt-even td.yui-dt-selected,
992 .yui-skin-sam tr.yui-dt-odd td.yui-dt-selected {
992 .yui-skin-sam tr.yui-dt-odd td.yui-dt-selected {
993 background-color: #446cd7;
993 background-color: #446cd7;
994 color: #FFF;
994 color: #FFF;
995 }
995 }
996 .yui-skin-sam .yui-dt-list th.yui-dt-selected,
996 .yui-skin-sam .yui-dt-list th.yui-dt-selected,
997 .yui-skin-sam .yui-dt-list th.yui-dt-selected a { background-color: #446cd7 }
997 .yui-skin-sam .yui-dt-list th.yui-dt-selected a { background-color: #446cd7 }
998 .yui-skin-sam .yui-dt-list tr.yui-dt-selected td,
998 .yui-skin-sam .yui-dt-list tr.yui-dt-selected td,
999 .yui-skin-sam .yui-dt-list tr.yui-dt-selected td.yui-dt-asc,
999 .yui-skin-sam .yui-dt-list tr.yui-dt-selected td.yui-dt-asc,
1000 .yui-skin-sam .yui-dt-list tr.yui-dt-selected td.yui-dt-desc {
1000 .yui-skin-sam .yui-dt-list tr.yui-dt-selected td.yui-dt-desc {
1001 background-color: #426fd9;
1001 background-color: #426fd9;
1002 color: #FFF;
1002 color: #FFF;
1003 }
1003 }
1004 .yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-selected,
1004 .yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-selected,
1005 .yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-selected {
1005 .yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-selected {
1006 background-color: #446cd7;
1006 background-color: #446cd7;
1007 color: #FFF;
1007 color: #FFF;
1008 }
1008 }
1009 .yui-skin-sam .yui-dt-paginator {
1009 .yui-skin-sam .yui-dt-paginator {
1010 display: block;
1010 display: block;
1011 margin: 6px 0;
1011 margin: 6px 0;
1012 white-space: nowrap;
1012 white-space: nowrap;
1013 }
1013 }
1014 .yui-skin-sam .yui-dt-paginator .yui-dt-first,
1014 .yui-skin-sam .yui-dt-paginator .yui-dt-first,
1015 .yui-skin-sam .yui-dt-paginator .yui-dt-last,
1015 .yui-skin-sam .yui-dt-paginator .yui-dt-last,
1016 .yui-skin-sam .yui-dt-paginator .yui-dt-selected { padding: 2px 6px }
1016 .yui-skin-sam .yui-dt-paginator .yui-dt-selected { padding: 2px 6px }
1017 .yui-skin-sam .yui-dt-paginator a.yui-dt-first,
1017 .yui-skin-sam .yui-dt-paginator a.yui-dt-first,
1018 .yui-skin-sam .yui-dt-paginator a.yui-dt-last { text-decoration: none }
1018 .yui-skin-sam .yui-dt-paginator a.yui-dt-last { text-decoration: none }
1019 .yui-skin-sam .yui-dt-paginator .yui-dt-previous,
1019 .yui-skin-sam .yui-dt-paginator .yui-dt-previous,
1020 .yui-skin-sam .yui-dt-paginator .yui-dt-next { display: none }
1020 .yui-skin-sam .yui-dt-paginator .yui-dt-next { display: none }
1021 .yui-skin-sam a.yui-dt-page {
1021 .yui-skin-sam a.yui-dt-page {
1022 border: 1px solid #cbcbcb;
1022 border: 1px solid #cbcbcb;
1023 padding: 2px 6px;
1023 padding: 2px 6px;
1024 text-decoration: none;
1024 text-decoration: none;
1025 background-color: #fff;
1025 background-color: #fff;
1026 }
1026 }
1027 .yui-skin-sam .yui-dt-selected {
1027 .yui-skin-sam .yui-dt-selected {
1028 border: 1px solid #fff;
1028 border: 1px solid #fff;
1029 background-color: #fff;
1029 background-color: #fff;
1030 }
1030 }
1031
1031
1032 #content #left {
1032 #content #left {
1033 left: 0;
1033 left: 0;
1034 width: 280px;
1034 width: 280px;
1035 position: absolute;
1035 position: absolute;
1036 }
1036 }
1037
1037
1038 #content #right {
1038 #content #right {
1039 margin: 0 60px 10px 290px;
1039 margin: 0 60px 10px 290px;
1040 }
1040 }
1041
1041
1042 #content div.box {
1042 #content div.box {
1043 clear: both;
1043 clear: both;
1044 overflow: hidden;
1044 overflow: hidden;
1045 background: #fff;
1045 background: #fff;
1046 margin: 0 0 10px;
1046 margin: 0 0 10px;
1047 padding: 0 0 10px;
1047 padding: 0 0 10px;
1048 -webkit-border-radius: 4px 4px 4px 4px;
1048 -webkit-border-radius: 4px 4px 4px 4px;
1049 -khtml-border-radius: 4px 4px 4px 4px;
1049 -khtml-border-radius: 4px 4px 4px 4px;
1050 -moz-border-radius: 4px 4px 4px 4px;
1050 -moz-border-radius: 4px 4px 4px 4px;
1051 border-radius: 4px 4px 4px 4px;
1051 border-radius: 4px 4px 4px 4px;
1052 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
1052 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
1053 }
1053 }
1054
1054
1055 #content div.box-left {
1055 #content div.box-left {
1056 width: 49%;
1056 width: 49%;
1057 clear: none;
1057 clear: none;
1058 float: left;
1058 float: left;
1059 margin: 0 0 10px;
1059 margin: 0 0 10px;
1060 }
1060 }
1061
1061
1062 #content div.box-right {
1062 #content div.box-right {
1063 width: 49%;
1063 width: 49%;
1064 clear: none;
1064 clear: none;
1065 float: right;
1065 float: right;
1066 margin: 0 0 10px;
1066 margin: 0 0 10px;
1067 }
1067 }
1068
1068
1069 #content div.box div.title {
1069 #content div.box div.title {
1070 clear: both;
1070 clear: both;
1071 overflow: hidden;
1071 overflow: hidden;
1072 background-color: #003B76;
1072 background-color: #003B76;
1073 background-repeat: repeat-x;
1073 background-repeat: repeat-x;
1074 background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
1074 background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
1075 background-image: -moz-linear-gradient(top, #003b76, #00376e);
1075 background-image: -moz-linear-gradient(top, #003b76, #00376e);
1076 background-image: -ms-linear-gradient(top, #003b76, #00376e);
1076 background-image: -ms-linear-gradient(top, #003b76, #00376e);
1077 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76), color-stop(100%, #00376e) );
1077 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76), color-stop(100%, #00376e) );
1078 background-image: -webkit-linear-gradient(top, #003b76, #00376e);
1078 background-image: -webkit-linear-gradient(top, #003b76, #00376e);
1079 background-image: -o-linear-gradient(top, #003b76, #00376e);
1079 background-image: -o-linear-gradient(top, #003b76, #00376e);
1080 background-image: linear-gradient(top, #003b76, #00376e);
1080 background-image: linear-gradient(top, #003b76, #00376e);
1081 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76', endColorstr='#00376e', GradientType=0 );
1081 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76', endColorstr='#00376e', GradientType=0 );
1082 margin: 0 0 20px;
1082 margin: 0 0 20px;
1083 padding: 0;
1083 padding: 0;
1084 }
1084 }
1085
1085
1086 #content div.box div.title h5 {
1086 #content div.box div.title h5 {
1087 float: left;
1087 float: left;
1088 border: none;
1088 border: none;
1089 color: #fff;
1089 color: #fff;
1090 text-transform: uppercase;
1090 text-transform: uppercase;
1091 margin: 0;
1091 margin: 0;
1092 padding: 11px 0 11px 10px;
1092 padding: 11px 0 11px 10px;
1093 }
1093 }
1094
1094
1095 #content div.box div.title .link-white{
1095 #content div.box div.title .link-white{
1096 color: #FFFFFF;
1096 color: #FFFFFF;
1097 }
1097 }
1098
1098
1099 #content div.box div.title .link-white.current{
1099 #content div.box div.title .link-white.current{
1100 color: #BFE3FF;
1100 color: #BFE3FF;
1101 }
1101 }
1102
1102
1103 #content div.box div.title ul.links li {
1103 #content div.box div.title ul.links li {
1104 list-style: none;
1104 list-style: none;
1105 float: left;
1105 float: left;
1106 margin: 0;
1106 margin: 0;
1107 padding: 0;
1107 padding: 0;
1108 }
1108 }
1109
1109
1110 #content div.box div.title ul.links li a {
1110 #content div.box div.title ul.links li a {
1111 border-left: 1px solid #316293;
1111 border-left: 1px solid #316293;
1112 color: #FFFFFF;
1112 color: #FFFFFF;
1113 display: block;
1113 display: block;
1114 float: left;
1114 float: left;
1115 font-size: 13px;
1115 font-size: 13px;
1116 font-weight: 700;
1116 font-weight: 700;
1117 height: 1%;
1117 height: 1%;
1118 margin: 0;
1118 margin: 0;
1119 padding: 11px 22px 12px;
1119 padding: 11px 22px 12px;
1120 text-decoration: none;
1120 text-decoration: none;
1121 }
1121 }
1122
1122
1123 #content div.box h1,#content div.box h2,#content div.box h3,#content div.box h4,#content div.box h5,#content div.box h6
1123 #content div.box h1,#content div.box h2,#content div.box h3,#content div.box h4,#content div.box h5,#content div.box h6
1124 {
1124 {
1125 clear: both;
1125 clear: both;
1126 overflow: hidden;
1126 overflow: hidden;
1127 border-bottom: 1px solid #DDD;
1127 border-bottom: 1px solid #DDD;
1128 margin: 10px 20px;
1128 margin: 10px 20px;
1129 padding: 0 0 15px;
1129 padding: 0 0 15px;
1130 }
1130 }
1131
1131
1132 #content div.box p {
1132 #content div.box p {
1133 color: #5f5f5f;
1133 color: #5f5f5f;
1134 font-size: 12px;
1134 font-size: 12px;
1135 line-height: 150%;
1135 line-height: 150%;
1136 margin: 0 24px 10px;
1136 margin: 0 24px 10px;
1137 padding: 0;
1137 padding: 0;
1138 }
1138 }
1139
1139
1140 #content div.box blockquote {
1140 #content div.box blockquote {
1141 border-left: 4px solid #DDD;
1141 border-left: 4px solid #DDD;
1142 color: #5f5f5f;
1142 color: #5f5f5f;
1143 font-size: 11px;
1143 font-size: 11px;
1144 line-height: 150%;
1144 line-height: 150%;
1145 margin: 0 34px;
1145 margin: 0 34px;
1146 padding: 0 0 0 14px;
1146 padding: 0 0 0 14px;
1147 }
1147 }
1148
1148
1149 #content div.box blockquote p {
1149 #content div.box blockquote p {
1150 margin: 10px 0;
1150 margin: 10px 0;
1151 padding: 0;
1151 padding: 0;
1152 }
1152 }
1153
1153
1154 #content div.box dl {
1154 #content div.box dl {
1155 margin: 10px 0px;
1155 margin: 10px 0px;
1156 }
1156 }
1157
1157
1158 #content div.box dt {
1158 #content div.box dt {
1159 font-size: 12px;
1159 font-size: 12px;
1160 margin: 0;
1160 margin: 0;
1161 }
1161 }
1162
1162
1163 #content div.box dd {
1163 #content div.box dd {
1164 font-size: 12px;
1164 font-size: 12px;
1165 margin: 0;
1165 margin: 0;
1166 padding: 8px 0 8px 15px;
1166 padding: 8px 0 8px 15px;
1167 }
1167 }
1168
1168
1169 #content div.box li {
1169 #content div.box li {
1170 font-size: 12px;
1170 font-size: 12px;
1171 padding: 4px 0;
1171 padding: 4px 0;
1172 }
1172 }
1173
1173
1174 #content div.box ul.disc,#content div.box ul.circle {
1174 #content div.box ul.disc,#content div.box ul.circle {
1175 margin: 10px 24px 10px 38px;
1175 margin: 10px 24px 10px 38px;
1176 }
1176 }
1177
1177
1178 #content div.box ul.square {
1178 #content div.box ul.square {
1179 margin: 10px 24px 10px 40px;
1179 margin: 10px 24px 10px 40px;
1180 }
1180 }
1181
1181
1182 #content div.box img.left {
1182 #content div.box img.left {
1183 border: none;
1183 border: none;
1184 float: left;
1184 float: left;
1185 margin: 10px 10px 10px 0;
1185 margin: 10px 10px 10px 0;
1186 }
1186 }
1187
1187
1188 #content div.box img.right {
1188 #content div.box img.right {
1189 border: none;
1189 border: none;
1190 float: right;
1190 float: right;
1191 margin: 10px 0 10px 10px;
1191 margin: 10px 0 10px 10px;
1192 }
1192 }
1193
1193
1194 #content div.box div.messages {
1194 #content div.box div.messages {
1195 clear: both;
1195 clear: both;
1196 overflow: hidden;
1196 overflow: hidden;
1197 margin: 0 20px;
1197 margin: 0 20px;
1198 padding: 0;
1198 padding: 0;
1199 }
1199 }
1200
1200
1201 #content div.box div.message {
1201 #content div.box div.message {
1202 clear: both;
1202 clear: both;
1203 overflow: hidden;
1203 overflow: hidden;
1204 margin: 0;
1204 margin: 0;
1205 padding: 5px 0;
1205 padding: 5px 0;
1206 white-space: pre-wrap;
1206 white-space: pre-wrap;
1207 }
1207 }
1208 #content div.box div.expand {
1208 #content div.box div.expand {
1209 width: 110%;
1209 width: 110%;
1210 height:14px;
1210 height:14px;
1211 font-size:10px;
1211 font-size:10px;
1212 text-align:center;
1212 text-align:center;
1213 cursor: pointer;
1213 cursor: pointer;
1214 color:#666;
1214 color:#666;
1215
1215
1216 background:-webkit-gradient(linear,0% 50%,100% 50%,color-stop(0%,rgba(255,255,255,0)),color-stop(100%,rgba(64,96,128,0.1)));
1216 background:-webkit-gradient(linear,0% 50%,100% 50%,color-stop(0%,rgba(255,255,255,0)),color-stop(100%,rgba(64,96,128,0.1)));
1217 background:-webkit-linear-gradient(top,rgba(255,255,255,0),rgba(64,96,128,0.1));
1217 background:-webkit-linear-gradient(top,rgba(255,255,255,0),rgba(64,96,128,0.1));
1218 background:-moz-linear-gradient(top,rgba(255,255,255,0),rgba(64,96,128,0.1));
1218 background:-moz-linear-gradient(top,rgba(255,255,255,0),rgba(64,96,128,0.1));
1219 background:-o-linear-gradient(top,rgba(255,255,255,0),rgba(64,96,128,0.1));
1219 background:-o-linear-gradient(top,rgba(255,255,255,0),rgba(64,96,128,0.1));
1220 background:-ms-linear-gradient(top,rgba(255,255,255,0),rgba(64,96,128,0.1));
1220 background:-ms-linear-gradient(top,rgba(255,255,255,0),rgba(64,96,128,0.1));
1221 background:linear-gradient(top,rgba(255,255,255,0),rgba(64,96,128,0.1));
1221 background:linear-gradient(top,rgba(255,255,255,0),rgba(64,96,128,0.1));
1222
1222
1223 display: none;
1223 display: none;
1224 }
1224 }
1225 #content div.box div.expand .expandtext {
1225 #content div.box div.expand .expandtext {
1226 background-color: #ffffff;
1226 background-color: #ffffff;
1227 padding: 2px;
1227 padding: 2px;
1228 border-radius: 2px;
1228 border-radius: 2px;
1229 }
1229 }
1230
1230
1231 #content div.box div.message a {
1231 #content div.box div.message a {
1232 font-weight: 400 !important;
1232 font-weight: 400 !important;
1233 }
1233 }
1234
1234
1235 #content div.box div.message div.image {
1235 #content div.box div.message div.image {
1236 float: left;
1236 float: left;
1237 margin: 9px 0 0 5px;
1237 margin: 9px 0 0 5px;
1238 padding: 6px;
1238 padding: 6px;
1239 }
1239 }
1240
1240
1241 #content div.box div.message div.image img {
1241 #content div.box div.message div.image img {
1242 vertical-align: middle;
1242 vertical-align: middle;
1243 margin: 0;
1243 margin: 0;
1244 }
1244 }
1245
1245
1246 #content div.box div.message div.text {
1246 #content div.box div.message div.text {
1247 float: left;
1247 float: left;
1248 margin: 0;
1248 margin: 0;
1249 padding: 9px 6px;
1249 padding: 9px 6px;
1250 }
1250 }
1251
1251
1252 #content div.box div.message div.dismiss a {
1252 #content div.box div.message div.dismiss a {
1253 height: 16px;
1253 height: 16px;
1254 width: 16px;
1254 width: 16px;
1255 display: block;
1255 display: block;
1256 background: url("../images/icons/cross.png") no-repeat;
1256 background: url("../images/icons/cross.png") no-repeat;
1257 margin: 15px 14px 0 0;
1257 margin: 15px 14px 0 0;
1258 padding: 0;
1258 padding: 0;
1259 }
1259 }
1260
1260
1261 #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
1261 #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
1262 {
1262 {
1263 border: none;
1263 border: none;
1264 margin: 0;
1264 margin: 0;
1265 padding: 0;
1265 padding: 0;
1266 }
1266 }
1267
1267
1268 #content div.box div.message div.text span {
1268 #content div.box div.message div.text span {
1269 height: 1%;
1269 height: 1%;
1270 display: block;
1270 display: block;
1271 margin: 0;
1271 margin: 0;
1272 padding: 5px 0 0;
1272 padding: 5px 0 0;
1273 }
1273 }
1274
1274
1275 #content div.box div.message-error {
1275 #content div.box div.message-error {
1276 height: 1%;
1276 height: 1%;
1277 clear: both;
1277 clear: both;
1278 overflow: hidden;
1278 overflow: hidden;
1279 background: #FBE3E4;
1279 background: #FBE3E4;
1280 border: 1px solid #FBC2C4;
1280 border: 1px solid #FBC2C4;
1281 color: #860006;
1281 color: #860006;
1282 }
1282 }
1283
1283
1284 #content div.box div.message-error h6 {
1284 #content div.box div.message-error h6 {
1285 color: #860006;
1285 color: #860006;
1286 }
1286 }
1287
1287
1288 #content div.box div.message-warning {
1288 #content div.box div.message-warning {
1289 height: 1%;
1289 height: 1%;
1290 clear: both;
1290 clear: both;
1291 overflow: hidden;
1291 overflow: hidden;
1292 background: #FFF6BF;
1292 background: #FFF6BF;
1293 border: 1px solid #FFD324;
1293 border: 1px solid #FFD324;
1294 color: #5f5200;
1294 color: #5f5200;
1295 }
1295 }
1296
1296
1297 #content div.box div.message-warning h6 {
1297 #content div.box div.message-warning h6 {
1298 color: #5f5200;
1298 color: #5f5200;
1299 }
1299 }
1300
1300
1301 #content div.box div.message-notice {
1301 #content div.box div.message-notice {
1302 height: 1%;
1302 height: 1%;
1303 clear: both;
1303 clear: both;
1304 overflow: hidden;
1304 overflow: hidden;
1305 background: #8FBDE0;
1305 background: #8FBDE0;
1306 border: 1px solid #6BACDE;
1306 border: 1px solid #6BACDE;
1307 color: #003863;
1307 color: #003863;
1308 }
1308 }
1309
1309
1310 #content div.box div.message-notice h6 {
1310 #content div.box div.message-notice h6 {
1311 color: #003863;
1311 color: #003863;
1312 }
1312 }
1313
1313
1314 #content div.box div.message-success {
1314 #content div.box div.message-success {
1315 height: 1%;
1315 height: 1%;
1316 clear: both;
1316 clear: both;
1317 overflow: hidden;
1317 overflow: hidden;
1318 background: #E6EFC2;
1318 background: #E6EFC2;
1319 border: 1px solid #C6D880;
1319 border: 1px solid #C6D880;
1320 color: #4e6100;
1320 color: #4e6100;
1321 }
1321 }
1322
1322
1323 #content div.box div.message-success h6 {
1323 #content div.box div.message-success h6 {
1324 color: #4e6100;
1324 color: #4e6100;
1325 }
1325 }
1326
1326
1327 #content div.box div.form div.fields div.field {
1327 #content div.box div.form div.fields div.field {
1328 height: 1%;
1328 height: 1%;
1329 border-bottom: 1px solid #DDD;
1329 border-bottom: 1px solid #DDD;
1330 clear: both;
1330 clear: both;
1331 margin: 0;
1331 margin: 0;
1332 padding: 10px 0;
1332 padding: 10px 0;
1333 }
1333 }
1334
1334
1335 #content div.box div.form div.fields div.field-first {
1335 #content div.box div.form div.fields div.field-first {
1336 padding: 0 0 10px;
1336 padding: 0 0 10px;
1337 }
1337 }
1338
1338
1339 #content div.box div.form div.fields div.field-noborder {
1339 #content div.box div.form div.fields div.field-noborder {
1340 border-bottom: 0 !important;
1340 border-bottom: 0 !important;
1341 }
1341 }
1342
1342
1343 #content div.box div.form div.fields div.field span.error-message {
1343 #content div.box div.form div.fields div.field span.error-message {
1344 height: 1%;
1344 height: 1%;
1345 display: inline-block;
1345 display: inline-block;
1346 color: red;
1346 color: red;
1347 margin: 8px 0 0 4px;
1347 margin: 8px 0 0 4px;
1348 padding: 0;
1348 padding: 0;
1349 }
1349 }
1350
1350
1351 #content div.box div.form div.fields div.field span.success {
1351 #content div.box div.form div.fields div.field span.success {
1352 height: 1%;
1352 height: 1%;
1353 display: block;
1353 display: block;
1354 color: #316309;
1354 color: #316309;
1355 margin: 8px 0 0;
1355 margin: 8px 0 0;
1356 padding: 0;
1356 padding: 0;
1357 }
1357 }
1358
1358
1359 #content div.box div.form div.fields div.field div.label {
1359 #content div.box div.form div.fields div.field div.label {
1360 left: 70px;
1360 left: 70px;
1361 width: 155px;
1361 width: 155px;
1362 position: absolute;
1362 position: absolute;
1363 margin: 0;
1363 margin: 0;
1364 padding: 5px 0 0 0px;
1364 padding: 5px 0 0 0px;
1365 }
1365 }
1366
1366
1367 #content div.box div.form div.fields div.field div.label-summary {
1367 #content div.box div.form div.fields div.field div.label-summary {
1368 left: 30px;
1368 left: 30px;
1369 width: 155px;
1369 width: 155px;
1370 position: absolute;
1370 position: absolute;
1371 margin: 0;
1371 margin: 0;
1372 padding: 0px 0 0 0px;
1372 padding: 0px 0 0 0px;
1373 }
1373 }
1374
1374
1375 #content div.box-left div.form div.fields div.field div.label,
1375 #content div.box-left div.form div.fields div.field div.label,
1376 #content div.box-right div.form div.fields div.field div.label,
1376 #content div.box-right div.form div.fields div.field div.label,
1377 #content div.box-left div.form div.fields div.field div.label,
1377 #content div.box-left div.form div.fields div.field div.label,
1378 #content div.box-left div.form div.fields div.field div.label-summary,
1378 #content div.box-left div.form div.fields div.field div.label-summary,
1379 #content div.box-right div.form div.fields div.field div.label-summary,
1379 #content div.box-right div.form div.fields div.field div.label-summary,
1380 #content div.box-left div.form div.fields div.field div.label-summary
1380 #content div.box-left div.form div.fields div.field div.label-summary
1381 {
1381 {
1382 clear: both;
1382 clear: both;
1383 overflow: hidden;
1383 overflow: hidden;
1384 left: 0;
1384 left: 0;
1385 width: auto;
1385 width: auto;
1386 position: relative;
1386 position: relative;
1387 margin: 0;
1387 margin: 0;
1388 padding: 0 0 8px;
1388 padding: 0 0 8px;
1389 }
1389 }
1390
1390
1391 #content div.box div.form div.fields div.field div.label-select {
1391 #content div.box div.form div.fields div.field div.label-select {
1392 padding: 5px 0 0 5px;
1392 padding: 5px 0 0 5px;
1393 }
1393 }
1394
1394
1395 #content div.box-left div.form div.fields div.field div.label-select,
1395 #content div.box-left div.form div.fields div.field div.label-select,
1396 #content div.box-right div.form div.fields div.field div.label-select
1396 #content div.box-right div.form div.fields div.field div.label-select
1397 {
1397 {
1398 padding: 0 0 8px;
1398 padding: 0 0 8px;
1399 }
1399 }
1400
1400
1401 #content div.box-left div.form div.fields div.field div.label-textarea,
1401 #content div.box-left div.form div.fields div.field div.label-textarea,
1402 #content div.box-right div.form div.fields div.field div.label-textarea
1402 #content div.box-right div.form div.fields div.field div.label-textarea
1403 {
1403 {
1404 padding: 0 0 8px !important;
1404 padding: 0 0 8px !important;
1405 }
1405 }
1406
1406
1407 #content div.box div.form div.fields div.field div.label label,div.label label
1407 #content div.box div.form div.fields div.field div.label label,div.label label
1408 {
1408 {
1409 color: #393939;
1409 color: #393939;
1410 font-weight: 700;
1410 font-weight: 700;
1411 }
1411 }
1412 #content div.box div.form div.fields div.field div.label label,div.label-summary label
1412 #content div.box div.form div.fields div.field div.label label,div.label-summary label
1413 {
1413 {
1414 color: #393939;
1414 color: #393939;
1415 font-weight: 700;
1415 font-weight: 700;
1416 }
1416 }
1417 #content div.box div.form div.fields div.field div.input {
1417 #content div.box div.form div.fields div.field div.input {
1418 margin: 0 0 0 200px;
1418 margin: 0 0 0 200px;
1419 }
1419 }
1420
1420
1421 #content div.box div.form div.fields div.field div.input.summary {
1421 #content div.box div.form div.fields div.field div.input.summary {
1422 margin: 0 0 0 110px;
1422 margin: 0 0 0 110px;
1423 }
1423 }
1424 #content div.box div.form div.fields div.field div.input.summary-short {
1424 #content div.box div.form div.fields div.field div.input.summary-short {
1425 margin: 0 0 0 110px;
1425 margin: 0 0 0 110px;
1426 }
1426 }
1427 #content div.box div.form div.fields div.field div.file {
1427 #content div.box div.form div.fields div.field div.file {
1428 margin: 0 0 0 200px;
1428 margin: 0 0 0 200px;
1429 }
1429 }
1430
1430
1431 #content div.box-left div.form div.fields div.field div.input,#content div.box-right div.form div.fields div.field div.input
1431 #content div.box-left div.form div.fields div.field div.input,#content div.box-right div.form div.fields div.field div.input
1432 {
1432 {
1433 margin: 0 0 0 0px;
1433 margin: 0 0 0 0px;
1434 }
1434 }
1435
1435
1436 #content div.box div.form div.fields div.field div.input input,
1436 #content div.box div.form div.fields div.field div.input input,
1437 .reviewer_ac input {
1437 .reviewer_ac input {
1438 background: #FFF;
1438 background: #FFF;
1439 border-top: 1px solid #b3b3b3;
1439 border-top: 1px solid #b3b3b3;
1440 border-left: 1px solid #b3b3b3;
1440 border-left: 1px solid #b3b3b3;
1441 border-right: 1px solid #eaeaea;
1441 border-right: 1px solid #eaeaea;
1442 border-bottom: 1px solid #eaeaea;
1442 border-bottom: 1px solid #eaeaea;
1443 color: #000;
1443 color: #000;
1444 font-size: 11px;
1444 font-size: 11px;
1445 margin: 0;
1445 margin: 0;
1446 padding: 7px 7px 6px;
1446 padding: 7px 7px 6px;
1447 }
1447 }
1448
1448
1449 #content div.box div.form div.fields div.field div.input input#clone_url,
1449 #content div.box div.form div.fields div.field div.input input#clone_url,
1450 #content div.box div.form div.fields div.field div.input input#clone_url_id
1450 #content div.box div.form div.fields div.field div.input input#clone_url_id
1451 {
1451 {
1452 font-size: 16px;
1452 font-size: 16px;
1453 padding: 2px;
1453 padding: 2px;
1454 }
1454 }
1455
1455
1456 #content div.box div.form div.fields div.field div.file input {
1456 #content div.box div.form div.fields div.field div.file input {
1457 background: none repeat scroll 0 0 #FFFFFF;
1457 background: none repeat scroll 0 0 #FFFFFF;
1458 border-color: #B3B3B3 #EAEAEA #EAEAEA #B3B3B3;
1458 border-color: #B3B3B3 #EAEAEA #EAEAEA #B3B3B3;
1459 border-style: solid;
1459 border-style: solid;
1460 border-width: 1px;
1460 border-width: 1px;
1461 color: #000000;
1461 color: #000000;
1462 font-size: 11px;
1462 font-size: 11px;
1463 margin: 0;
1463 margin: 0;
1464 padding: 7px 7px 6px;
1464 padding: 7px 7px 6px;
1465 }
1465 }
1466
1466
1467 input.disabled {
1467 input.disabled {
1468 background-color: #F5F5F5 !important;
1468 background-color: #F5F5F5 !important;
1469 }
1469 }
1470 #content div.box div.form div.fields div.field div.input input.small {
1470 #content div.box div.form div.fields div.field div.input input.small {
1471 width: 30%;
1471 width: 30%;
1472 }
1472 }
1473
1473
1474 #content div.box div.form div.fields div.field div.input input.medium {
1474 #content div.box div.form div.fields div.field div.input input.medium {
1475 width: 55%;
1475 width: 55%;
1476 }
1476 }
1477
1477
1478 #content div.box div.form div.fields div.field div.input input.large {
1478 #content div.box div.form div.fields div.field div.input input.large {
1479 width: 85%;
1479 width: 85%;
1480 }
1480 }
1481
1481
1482 #content div.box div.form div.fields div.field div.input input.date {
1482 #content div.box div.form div.fields div.field div.input input.date {
1483 width: 177px;
1483 width: 177px;
1484 }
1484 }
1485
1485
1486 #content div.box div.form div.fields div.field div.input input.button {
1486 #content div.box div.form div.fields div.field div.input input.button {
1487 background: #D4D0C8;
1487 background: #D4D0C8;
1488 border-top: 1px solid #FFF;
1488 border-top: 1px solid #FFF;
1489 border-left: 1px solid #FFF;
1489 border-left: 1px solid #FFF;
1490 border-right: 1px solid #404040;
1490 border-right: 1px solid #404040;
1491 border-bottom: 1px solid #404040;
1491 border-bottom: 1px solid #404040;
1492 color: #000;
1492 color: #000;
1493 margin: 0;
1493 margin: 0;
1494 padding: 4px 8px;
1494 padding: 4px 8px;
1495 }
1495 }
1496
1496
1497 #content div.box div.form div.fields div.field div.textarea {
1497 #content div.box div.form div.fields div.field div.textarea {
1498 border-top: 1px solid #b3b3b3;
1498 border-top: 1px solid #b3b3b3;
1499 border-left: 1px solid #b3b3b3;
1499 border-left: 1px solid #b3b3b3;
1500 border-right: 1px solid #eaeaea;
1500 border-right: 1px solid #eaeaea;
1501 border-bottom: 1px solid #eaeaea;
1501 border-bottom: 1px solid #eaeaea;
1502 margin: 0 0 0 200px;
1502 margin: 0 0 0 200px;
1503 padding: 10px;
1503 padding: 10px;
1504 }
1504 }
1505
1505
1506 #content div.box div.form div.fields div.field div.textarea-editor {
1506 #content div.box div.form div.fields div.field div.textarea-editor {
1507 border: 1px solid #ddd;
1507 border: 1px solid #ddd;
1508 padding: 0;
1508 padding: 0;
1509 }
1509 }
1510
1510
1511 #content div.box div.form div.fields div.field div.textarea textarea {
1511 #content div.box div.form div.fields div.field div.textarea textarea {
1512 width: 100%;
1512 width: 100%;
1513 height: 220px;
1513 height: 220px;
1514 overflow: hidden;
1514 overflow: hidden;
1515 background: #FFF;
1515 background: #FFF;
1516 color: #000;
1516 color: #000;
1517 font-size: 11px;
1517 font-size: 11px;
1518 outline: none;
1518 outline: none;
1519 border-width: 0;
1519 border-width: 0;
1520 margin: 0;
1520 margin: 0;
1521 padding: 0;
1521 padding: 0;
1522 }
1522 }
1523
1523
1524 #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
1524 #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
1525 {
1525 {
1526 width: 100%;
1526 width: 100%;
1527 height: 100px;
1527 height: 100px;
1528 }
1528 }
1529
1529
1530 #content div.box div.form div.fields div.field div.textarea table {
1530 #content div.box div.form div.fields div.field div.textarea table {
1531 width: 100%;
1531 width: 100%;
1532 border: none;
1532 border: none;
1533 margin: 0;
1533 margin: 0;
1534 padding: 0;
1534 padding: 0;
1535 }
1535 }
1536
1536
1537 #content div.box div.form div.fields div.field div.textarea table td {
1537 #content div.box div.form div.fields div.field div.textarea table td {
1538 background: #DDD;
1538 background: #DDD;
1539 border: none;
1539 border: none;
1540 padding: 0;
1540 padding: 0;
1541 }
1541 }
1542
1542
1543 #content div.box div.form div.fields div.field div.textarea table td table
1543 #content div.box div.form div.fields div.field div.textarea table td table
1544 {
1544 {
1545 width: auto;
1545 width: auto;
1546 border: none;
1546 border: none;
1547 margin: 0;
1547 margin: 0;
1548 padding: 0;
1548 padding: 0;
1549 }
1549 }
1550
1550
1551 #content div.box div.form div.fields div.field div.textarea table td table td
1551 #content div.box div.form div.fields div.field div.textarea table td table td
1552 {
1552 {
1553 font-size: 11px;
1553 font-size: 11px;
1554 padding: 5px 5px 5px 0;
1554 padding: 5px 5px 5px 0;
1555 }
1555 }
1556
1556
1557 #content div.box div.form div.fields div.field input[type=text]:focus,
1557 #content div.box div.form div.fields div.field input[type=text]:focus,
1558 #content div.box div.form div.fields div.field input[type=password]:focus,
1558 #content div.box div.form div.fields div.field input[type=password]:focus,
1559 #content div.box div.form div.fields div.field input[type=file]:focus,
1559 #content div.box div.form div.fields div.field input[type=file]:focus,
1560 #content div.box div.form div.fields div.field textarea:focus,
1560 #content div.box div.form div.fields div.field textarea:focus,
1561 #content div.box div.form div.fields div.field select:focus,
1561 #content div.box div.form div.fields div.field select:focus,
1562 .reviewer_ac input:focus
1562 .reviewer_ac input:focus
1563 {
1563 {
1564 background: #f6f6f6;
1564 background: #f6f6f6;
1565 border-color: #666;
1565 border-color: #666;
1566 }
1566 }
1567
1567
1568 .reviewer_ac {
1568 .reviewer_ac {
1569 padding:10px
1569 padding:10px
1570 }
1570 }
1571
1571
1572 div.form div.fields div.field div.button {
1572 div.form div.fields div.field div.button {
1573 margin: 0;
1573 margin: 0;
1574 padding: 0 0 0 8px;
1574 padding: 0 0 0 8px;
1575 }
1575 }
1576 #content div.box table.noborder {
1576 #content div.box table.noborder {
1577 border: 1px solid transparent;
1577 border: 1px solid transparent;
1578 }
1578 }
1579
1579
1580 #content div.box table {
1580 #content div.box table {
1581 width: 100%;
1581 width: 100%;
1582 border-collapse: separate;
1582 border-collapse: separate;
1583 margin: 0;
1583 margin: 0;
1584 padding: 0;
1584 padding: 0;
1585 border: 1px solid #eee;
1585 border: 1px solid #eee;
1586 -webkit-border-radius: 4px;
1586 -webkit-border-radius: 4px;
1587 -moz-border-radius: 4px;
1587 -moz-border-radius: 4px;
1588 border-radius: 4px;
1588 border-radius: 4px;
1589 }
1589 }
1590
1590
1591 #content div.box table th {
1591 #content div.box table th {
1592 background: #eee;
1592 background: #eee;
1593 border-bottom: 1px solid #ddd;
1593 border-bottom: 1px solid #ddd;
1594 padding: 5px 0px 5px 5px;
1594 padding: 5px 0px 5px 5px;
1595 }
1595 }
1596
1596
1597 #content div.box table th.left {
1597 #content div.box table th.left {
1598 text-align: left;
1598 text-align: left;
1599 }
1599 }
1600
1600
1601 #content div.box table th.right {
1601 #content div.box table th.right {
1602 text-align: right;
1602 text-align: right;
1603 }
1603 }
1604
1604
1605 #content div.box table th.center {
1605 #content div.box table th.center {
1606 text-align: center;
1606 text-align: center;
1607 }
1607 }
1608
1608
1609 #content div.box table th.selected {
1609 #content div.box table th.selected {
1610 vertical-align: middle;
1610 vertical-align: middle;
1611 padding: 0;
1611 padding: 0;
1612 }
1612 }
1613
1613
1614 #content div.box table td {
1614 #content div.box table td {
1615 background: #fff;
1615 background: #fff;
1616 border-bottom: 1px solid #cdcdcd;
1616 border-bottom: 1px solid #cdcdcd;
1617 vertical-align: middle;
1617 vertical-align: middle;
1618 padding: 5px;
1618 padding: 5px;
1619 }
1619 }
1620
1620
1621 #content div.box table tr.selected td {
1621 #content div.box table tr.selected td {
1622 background: #FFC;
1622 background: #FFC;
1623 }
1623 }
1624
1624
1625 #content div.box table td.selected {
1625 #content div.box table td.selected {
1626 width: 3%;
1626 width: 3%;
1627 text-align: center;
1627 text-align: center;
1628 vertical-align: middle;
1628 vertical-align: middle;
1629 padding: 0;
1629 padding: 0;
1630 }
1630 }
1631
1631
1632 #content div.box table td.action {
1632 #content div.box table td.action {
1633 width: 45%;
1633 width: 45%;
1634 text-align: left;
1634 text-align: left;
1635 }
1635 }
1636
1636
1637 #content div.box table td.date {
1637 #content div.box table td.date {
1638 width: 33%;
1638 width: 33%;
1639 text-align: center;
1639 text-align: center;
1640 }
1640 }
1641
1641
1642 #content div.box div.action {
1642 #content div.box div.action {
1643 float: right;
1643 float: right;
1644 background: #FFF;
1644 background: #FFF;
1645 text-align: right;
1645 text-align: right;
1646 margin: 10px 0 0;
1646 margin: 10px 0 0;
1647 padding: 0;
1647 padding: 0;
1648 }
1648 }
1649
1649
1650 #content div.box div.action select {
1650 #content div.box div.action select {
1651 font-size: 11px;
1651 font-size: 11px;
1652 margin: 0;
1652 margin: 0;
1653 }
1653 }
1654
1654
1655 #content div.box div.action .ui-selectmenu {
1655 #content div.box div.action .ui-selectmenu {
1656 margin: 0;
1656 margin: 0;
1657 padding: 0;
1657 padding: 0;
1658 }
1658 }
1659
1659
1660 #content div.box div.pagination {
1660 #content div.box div.pagination {
1661 height: 1%;
1661 height: 1%;
1662 clear: both;
1662 clear: both;
1663 overflow: hidden;
1663 overflow: hidden;
1664 margin: 10px 0 0;
1664 margin: 10px 0 0;
1665 padding: 0;
1665 padding: 0;
1666 }
1666 }
1667
1667
1668 #content div.box div.pagination ul.pager {
1668 #content div.box div.pagination ul.pager {
1669 float: right;
1669 float: right;
1670 text-align: right;
1670 text-align: right;
1671 margin: 0;
1671 margin: 0;
1672 padding: 0;
1672 padding: 0;
1673 }
1673 }
1674
1674
1675 #content div.box div.pagination ul.pager li {
1675 #content div.box div.pagination ul.pager li {
1676 height: 1%;
1676 height: 1%;
1677 float: left;
1677 float: left;
1678 list-style: none;
1678 list-style: none;
1679 background: #ebebeb url("../images/pager.png") repeat-x;
1679 background: #ebebeb url("../images/pager.png") repeat-x;
1680 border-top: 1px solid #dedede;
1680 border-top: 1px solid #dedede;
1681 border-left: 1px solid #cfcfcf;
1681 border-left: 1px solid #cfcfcf;
1682 border-right: 1px solid #c4c4c4;
1682 border-right: 1px solid #c4c4c4;
1683 border-bottom: 1px solid #c4c4c4;
1683 border-bottom: 1px solid #c4c4c4;
1684 color: #4A4A4A;
1684 color: #4A4A4A;
1685 font-weight: 700;
1685 font-weight: 700;
1686 margin: 0 0 0 4px;
1686 margin: 0 0 0 4px;
1687 padding: 0;
1687 padding: 0;
1688 }
1688 }
1689
1689
1690 #content div.box div.pagination ul.pager li.separator {
1690 #content div.box div.pagination ul.pager li.separator {
1691 padding: 6px;
1691 padding: 6px;
1692 }
1692 }
1693
1693
1694 #content div.box div.pagination ul.pager li.current {
1694 #content div.box div.pagination ul.pager li.current {
1695 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1695 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1696 border-top: 1px solid #ccc;
1696 border-top: 1px solid #ccc;
1697 border-left: 1px solid #bebebe;
1697 border-left: 1px solid #bebebe;
1698 border-right: 1px solid #b1b1b1;
1698 border-right: 1px solid #b1b1b1;
1699 border-bottom: 1px solid #afafaf;
1699 border-bottom: 1px solid #afafaf;
1700 color: #515151;
1700 color: #515151;
1701 padding: 6px;
1701 padding: 6px;
1702 }
1702 }
1703
1703
1704 #content div.box div.pagination ul.pager li a {
1704 #content div.box div.pagination ul.pager li a {
1705 height: 1%;
1705 height: 1%;
1706 display: block;
1706 display: block;
1707 float: left;
1707 float: left;
1708 color: #515151;
1708 color: #515151;
1709 text-decoration: none;
1709 text-decoration: none;
1710 margin: 0;
1710 margin: 0;
1711 padding: 6px;
1711 padding: 6px;
1712 }
1712 }
1713
1713
1714 #content div.box div.pagination ul.pager li a:hover,#content div.box div.pagination ul.pager li a:active
1714 #content div.box div.pagination ul.pager li a:hover,#content div.box div.pagination ul.pager li a:active
1715 {
1715 {
1716 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1716 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1717 border-top: 1px solid #ccc;
1717 border-top: 1px solid #ccc;
1718 border-left: 1px solid #bebebe;
1718 border-left: 1px solid #bebebe;
1719 border-right: 1px solid #b1b1b1;
1719 border-right: 1px solid #b1b1b1;
1720 border-bottom: 1px solid #afafaf;
1720 border-bottom: 1px solid #afafaf;
1721 margin: -1px;
1721 margin: -1px;
1722 }
1722 }
1723
1723
1724 #content div.box div.pagination-wh {
1724 #content div.box div.pagination-wh {
1725 height: 1%;
1725 height: 1%;
1726 clear: both;
1726 clear: both;
1727 overflow: hidden;
1727 overflow: hidden;
1728 text-align: right;
1728 text-align: right;
1729 margin: 10px 0 0;
1729 margin: 10px 0 0;
1730 padding: 0;
1730 padding: 0;
1731 }
1731 }
1732
1732
1733 #content div.box div.pagination-right {
1733 #content div.box div.pagination-right {
1734 float: right;
1734 float: right;
1735 }
1735 }
1736
1736
1737 #content div.box div.pagination-wh a,
1737 #content div.box div.pagination-wh a,
1738 #content div.box div.pagination-wh span.pager_dotdot,
1738 #content div.box div.pagination-wh span.pager_dotdot,
1739 #content div.box div.pagination-wh span.yui-pg-previous,
1739 #content div.box div.pagination-wh span.yui-pg-previous,
1740 #content div.box div.pagination-wh span.yui-pg-last,
1740 #content div.box div.pagination-wh span.yui-pg-last,
1741 #content div.box div.pagination-wh span.yui-pg-next,
1741 #content div.box div.pagination-wh span.yui-pg-next,
1742 #content div.box div.pagination-wh span.yui-pg-first
1742 #content div.box div.pagination-wh span.yui-pg-first
1743 {
1743 {
1744 height: 1%;
1744 height: 1%;
1745 float: left;
1745 float: left;
1746 background: #ebebeb url("../images/pager.png") repeat-x;
1746 background: #ebebeb url("../images/pager.png") repeat-x;
1747 border-top: 1px solid #dedede;
1747 border-top: 1px solid #dedede;
1748 border-left: 1px solid #cfcfcf;
1748 border-left: 1px solid #cfcfcf;
1749 border-right: 1px solid #c4c4c4;
1749 border-right: 1px solid #c4c4c4;
1750 border-bottom: 1px solid #c4c4c4;
1750 border-bottom: 1px solid #c4c4c4;
1751 color: #4A4A4A;
1751 color: #4A4A4A;
1752 font-weight: 700;
1752 font-weight: 700;
1753 margin: 0 0 0 4px;
1753 margin: 0 0 0 4px;
1754 padding: 6px;
1754 padding: 6px;
1755 }
1755 }
1756
1756
1757 #content div.box div.pagination-wh span.pager_curpage {
1757 #content div.box div.pagination-wh span.pager_curpage {
1758 height: 1%;
1758 height: 1%;
1759 float: left;
1759 float: left;
1760 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1760 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1761 border-top: 1px solid #ccc;
1761 border-top: 1px solid #ccc;
1762 border-left: 1px solid #bebebe;
1762 border-left: 1px solid #bebebe;
1763 border-right: 1px solid #b1b1b1;
1763 border-right: 1px solid #b1b1b1;
1764 border-bottom: 1px solid #afafaf;
1764 border-bottom: 1px solid #afafaf;
1765 color: #515151;
1765 color: #515151;
1766 font-weight: 700;
1766 font-weight: 700;
1767 margin: 0 0 0 4px;
1767 margin: 0 0 0 4px;
1768 padding: 6px;
1768 padding: 6px;
1769 }
1769 }
1770
1770
1771 #content div.box div.pagination-wh a:hover,#content div.box div.pagination-wh a:active
1771 #content div.box div.pagination-wh a:hover,#content div.box div.pagination-wh a:active
1772 {
1772 {
1773 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1773 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1774 border-top: 1px solid #ccc;
1774 border-top: 1px solid #ccc;
1775 border-left: 1px solid #bebebe;
1775 border-left: 1px solid #bebebe;
1776 border-right: 1px solid #b1b1b1;
1776 border-right: 1px solid #b1b1b1;
1777 border-bottom: 1px solid #afafaf;
1777 border-bottom: 1px solid #afafaf;
1778 text-decoration: none;
1778 text-decoration: none;
1779 }
1779 }
1780
1780
1781 #content div.box div.traffic div.legend {
1781 #content div.box div.traffic div.legend {
1782 clear: both;
1782 clear: both;
1783 overflow: hidden;
1783 overflow: hidden;
1784 border-bottom: 1px solid #ddd;
1784 border-bottom: 1px solid #ddd;
1785 margin: 0 0 10px;
1785 margin: 0 0 10px;
1786 padding: 0 0 10px;
1786 padding: 0 0 10px;
1787 }
1787 }
1788
1788
1789 #content div.box div.traffic div.legend h6 {
1789 #content div.box div.traffic div.legend h6 {
1790 float: left;
1790 float: left;
1791 border: none;
1791 border: none;
1792 margin: 0;
1792 margin: 0;
1793 padding: 0;
1793 padding: 0;
1794 }
1794 }
1795
1795
1796 #content div.box div.traffic div.legend li {
1796 #content div.box div.traffic div.legend li {
1797 list-style: none;
1797 list-style: none;
1798 float: left;
1798 float: left;
1799 font-size: 11px;
1799 font-size: 11px;
1800 margin: 0;
1800 margin: 0;
1801 padding: 0 8px 0 4px;
1801 padding: 0 8px 0 4px;
1802 }
1802 }
1803
1803
1804 #content div.box div.traffic div.legend li.visits {
1804 #content div.box div.traffic div.legend li.visits {
1805 border-left: 12px solid #edc240;
1805 border-left: 12px solid #edc240;
1806 }
1806 }
1807
1807
1808 #content div.box div.traffic div.legend li.pageviews {
1808 #content div.box div.traffic div.legend li.pageviews {
1809 border-left: 12px solid #afd8f8;
1809 border-left: 12px solid #afd8f8;
1810 }
1810 }
1811
1811
1812 #content div.box div.traffic table {
1812 #content div.box div.traffic table {
1813 width: auto;
1813 width: auto;
1814 }
1814 }
1815
1815
1816 #content div.box div.traffic table td {
1816 #content div.box div.traffic table td {
1817 background: transparent;
1817 background: transparent;
1818 border: none;
1818 border: none;
1819 padding: 2px 3px 3px;
1819 padding: 2px 3px 3px;
1820 }
1820 }
1821
1821
1822 #content div.box div.traffic table td.legendLabel {
1822 #content div.box div.traffic table td.legendLabel {
1823 padding: 0 3px 2px;
1823 padding: 0 3px 2px;
1824 }
1824 }
1825
1825
1826 #summary {
1826 #summary {
1827
1827
1828 }
1828 }
1829
1829
1830 #summary .metatag {
1831 display: inline-block;
1832 padding: 3px 5px;
1833 margin-bottom: 3px;
1834 margin-right: 1px;
1835 border-radius: 5px;
1836 }
1837
1838 #content div.box #summary p {
1839 margin-bottom: -5px;
1840 width: 600px;
1841 white-space: pre-wrap;
1842 }
1843
1844 #content div.box #summary p:last-child {
1845 margin-bottom: 9px;
1846 }
1847
1848 #content div.box #summary p:first-of-type {
1849 margin-top: 9px;
1850 }
1851
1852 .metatag {
1853 display: inline-block;
1854 margin-right: 1px;
1855 -webkit-border-radius: 4px 4px 4px 4px;
1856 -khtml-border-radius: 4px 4px 4px 4px;
1857 -moz-border-radius: 4px 4px 4px 4px;
1858 border-radius: 4px 4px 4px 4px;
1859
1860 border: solid 1px #9CF;
1861 padding: 2px 3px 2px 3px !important;
1862 background-color: #DEF;
1863 }
1864
1865 .metatag[tag="dead"] {
1866 background-color: #E44;
1867 }
1868
1869 .metatag[tag="stale"] {
1870 background-color: #EA4;
1871 }
1872
1873 .metatag[tag="featured"] {
1874 background-color: #AEA;
1875 }
1876
1877 .metatag[tag="requires"] {
1878 background-color: #9CF;
1879 }
1880
1881 .metatag[tag="recommends"] {
1882 background-color: #BDF;
1883 }
1884
1885 .metatag[tag="lang"] {
1886 background-color: #FAF474;
1887 }
1888
1889 .metatag[tag="license"] {
1890 border: solid 1px #9CF;
1891 background-color: #DEF;
1892 target-new: tab !important;
1893 }
1894 .metatag[tag="see"] {
1895 border: solid 1px #CBD;
1896 background-color: #EDF;
1897 }
1898
1899 a.metatag[tag="license"]:hover {
1900 background-color: #003367;
1901 color: #FFF;
1902 text-decoration: none;
1903 }
1904
1830 #summary .desc {
1905 #summary .desc {
1831 white-space: pre;
1906 white-space: pre;
1832 width: 100%;
1907 width: 100%;
1833 }
1908 }
1834
1909
1835 #summary .repo_name {
1910 #summary .repo_name {
1836 font-size: 1.6em;
1911 font-size: 1.6em;
1837 font-weight: bold;
1912 font-weight: bold;
1838 vertical-align: baseline;
1913 vertical-align: baseline;
1839 clear: right
1914 clear: right
1840 }
1915 }
1841
1916
1842 #footer {
1917 #footer {
1843 clear: both;
1918 clear: both;
1844 overflow: hidden;
1919 overflow: hidden;
1845 text-align: right;
1920 text-align: right;
1846 margin: 0;
1921 margin: 0;
1847 padding: 0 10px 4px;
1922 padding: 0 10px 4px;
1848 margin: -10px 0 0;
1923 margin: -10px 0 0;
1849 }
1924 }
1850
1925
1851 #footer div#footer-inner {
1926 #footer div#footer-inner {
1852 background-color: #003B76;
1927 background-color: #003B76;
1853 background-repeat : repeat-x;
1928 background-repeat : repeat-x;
1854 background-image : -khtml-gradient( linear, left top, left bottom, from(#003B76), to(#00376E));
1929 background-image : -khtml-gradient( linear, left top, left bottom, from(#003B76), to(#00376E));
1855 background-image : -moz-linear-gradient(top, #003b76, #00376e);
1930 background-image : -moz-linear-gradient(top, #003b76, #00376e);
1856 background-image : -ms-linear-gradient( top, #003b76, #00376e);
1931 background-image : -ms-linear-gradient( top, #003b76, #00376e);
1857 background-image : -webkit-gradient( linear, left top, left bottom, color-stop( 0%, #003b76), color-stop( 100%, #00376e));
1932 background-image : -webkit-gradient( linear, left top, left bottom, color-stop( 0%, #003b76), color-stop( 100%, #00376e));
1858 background-image : -webkit-linear-gradient( top, #003b76, #00376e));
1933 background-image : -webkit-linear-gradient( top, #003b76, #00376e));
1859 background-image : -o-linear-gradient( top, #003b76, #00376e));
1934 background-image : -o-linear-gradient( top, #003b76, #00376e));
1860 background-image : linear-gradient( top, #003b76, #00376e);
1935 background-image : linear-gradient( top, #003b76, #00376e);
1861 filter :progid : DXImageTransform.Microsoft.gradient ( startColorstr = '#003b76', endColorstr = '#00376e', GradientType = 0);
1936 filter :progid : DXImageTransform.Microsoft.gradient ( startColorstr = '#003b76', endColorstr = '#00376e', GradientType = 0);
1862 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
1937 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
1863 -webkit-border-radius: 4px 4px 4px 4px;
1938 -webkit-border-radius: 4px 4px 4px 4px;
1864 -khtml-border-radius: 4px 4px 4px 4px;
1939 -khtml-border-radius: 4px 4px 4px 4px;
1865 -moz-border-radius: 4px 4px 4px 4px;
1940 -moz-border-radius: 4px 4px 4px 4px;
1866 border-radius: 4px 4px 4px 4px;
1941 border-radius: 4px 4px 4px 4px;
1867 }
1942 }
1868
1943
1869 #footer div#footer-inner p {
1944 #footer div#footer-inner p {
1870 padding: 15px 25px 15px 0;
1945 padding: 15px 25px 15px 0;
1871 color: #FFF;
1946 color: #FFF;
1872 font-weight: 700;
1947 font-weight: 700;
1873 }
1948 }
1874
1949
1875 #footer div#footer-inner .footer-link {
1950 #footer div#footer-inner .footer-link {
1876 float: left;
1951 float: left;
1877 padding-left: 10px;
1952 padding-left: 10px;
1878 }
1953 }
1879
1954
1880 #footer div#footer-inner .footer-link a,#footer div#footer-inner .footer-link-right a
1955 #footer div#footer-inner .footer-link a,#footer div#footer-inner .footer-link-right a
1881 {
1956 {
1882 color: #FFF;
1957 color: #FFF;
1883 }
1958 }
1884
1959
1885 #login div.title {
1960 #login div.title {
1886 width: 420px;
1961 width: 420px;
1887 clear: both;
1962 clear: both;
1888 overflow: hidden;
1963 overflow: hidden;
1889 position: relative;
1964 position: relative;
1890 background-color: #003B76;
1965 background-color: #003B76;
1891 background-repeat : repeat-x;
1966 background-repeat : repeat-x;
1892 background-image : -khtml-gradient( linear, left top, left bottom, from(#003B76), to(#00376E));
1967 background-image : -khtml-gradient( linear, left top, left bottom, from(#003B76), to(#00376E));
1893 background-image : -moz-linear-gradient( top, #003b76, #00376e);
1968 background-image : -moz-linear-gradient( top, #003b76, #00376e);
1894 background-image : -ms-linear-gradient( top, #003b76, #00376e);
1969 background-image : -ms-linear-gradient( top, #003b76, #00376e);
1895 background-image : -webkit-gradient( linear, left top, left bottom, color-stop( 0%, #003b76), color-stop( 100%, #00376e));
1970 background-image : -webkit-gradient( linear, left top, left bottom, color-stop( 0%, #003b76), color-stop( 100%, #00376e));
1896 background-image : -webkit-linear-gradient( top, #003b76, #00376e));
1971 background-image : -webkit-linear-gradient( top, #003b76, #00376e));
1897 background-image : -o-linear-gradient( top, #003b76, #00376e));
1972 background-image : -o-linear-gradient( top, #003b76, #00376e));
1898 background-image : linear-gradient( top, #003b76, #00376e);
1973 background-image : linear-gradient( top, #003b76, #00376e);
1899 filter : progid : DXImageTransform.Microsoft.gradient ( startColorstr = '#003b76', endColorstr = '#00376e', GradientType = 0);
1974 filter : progid : DXImageTransform.Microsoft.gradient ( startColorstr = '#003b76', endColorstr = '#00376e', GradientType = 0);
1900 margin: 0 auto;
1975 margin: 0 auto;
1901 padding: 0;
1976 padding: 0;
1902 }
1977 }
1903
1978
1904 #login div.inner {
1979 #login div.inner {
1905 width: 380px;
1980 width: 380px;
1906 background: #FFF url("../images/login.png") no-repeat top left;
1981 background: #FFF url("../images/login.png") no-repeat top left;
1907 border-top: none;
1982 border-top: none;
1908 border-bottom: none;
1983 border-bottom: none;
1909 margin: 0 auto;
1984 margin: 0 auto;
1910 padding: 20px;
1985 padding: 20px;
1911 }
1986 }
1912
1987
1913 #login div.form div.fields div.field div.label {
1988 #login div.form div.fields div.field div.label {
1914 width: 173px;
1989 width: 173px;
1915 float: left;
1990 float: left;
1916 text-align: right;
1991 text-align: right;
1917 margin: 2px 10px 0 0;
1992 margin: 2px 10px 0 0;
1918 padding: 5px 0 0 5px;
1993 padding: 5px 0 0 5px;
1919 }
1994 }
1920
1995
1921 #login div.form div.fields div.field div.input input {
1996 #login div.form div.fields div.field div.input input {
1922 width: 176px;
1997 width: 176px;
1923 background: #FFF;
1998 background: #FFF;
1924 border-top: 1px solid #b3b3b3;
1999 border-top: 1px solid #b3b3b3;
1925 border-left: 1px solid #b3b3b3;
2000 border-left: 1px solid #b3b3b3;
1926 border-right: 1px solid #eaeaea;
2001 border-right: 1px solid #eaeaea;
1927 border-bottom: 1px solid #eaeaea;
2002 border-bottom: 1px solid #eaeaea;
1928 color: #000;
2003 color: #000;
1929 font-size: 11px;
2004 font-size: 11px;
1930 margin: 0;
2005 margin: 0;
1931 padding: 7px 7px 6px;
2006 padding: 7px 7px 6px;
1932 }
2007 }
1933
2008
1934 #login div.form div.fields div.buttons {
2009 #login div.form div.fields div.buttons {
1935 clear: both;
2010 clear: both;
1936 overflow: hidden;
2011 overflow: hidden;
1937 border-top: 1px solid #DDD;
2012 border-top: 1px solid #DDD;
1938 text-align: right;
2013 text-align: right;
1939 margin: 0;
2014 margin: 0;
1940 padding: 10px 0 0;
2015 padding: 10px 0 0;
1941 }
2016 }
1942
2017
1943 #login div.form div.links {
2018 #login div.form div.links {
1944 clear: both;
2019 clear: both;
1945 overflow: hidden;
2020 overflow: hidden;
1946 margin: 10px 0 0;
2021 margin: 10px 0 0;
1947 padding: 0 0 2px;
2022 padding: 0 0 2px;
1948 }
2023 }
1949
2024
1950 .user-menu{
2025 .user-menu{
1951 margin: 0px !important;
2026 margin: 0px !important;
1952 float: left;
2027 float: left;
1953 }
2028 }
1954
2029
1955 .user-menu .container{
2030 .user-menu .container{
1956 padding:0px 4px 0px 4px;
2031 padding:0px 4px 0px 4px;
1957 margin: 0px 0px 0px 0px;
2032 margin: 0px 0px 0px 0px;
1958 }
2033 }
1959
2034
1960 .user-menu .gravatar{
2035 .user-menu .gravatar{
1961 margin: 0px 0px 0px 0px;
2036 margin: 0px 0px 0px 0px;
1962 cursor: pointer;
2037 cursor: pointer;
1963 }
2038 }
1964 .user-menu .gravatar.enabled{
2039 .user-menu .gravatar.enabled{
1965 background-color: #FDF784 !important;
2040 background-color: #FDF784 !important;
1966 }
2041 }
1967 .user-menu .gravatar:hover{
2042 .user-menu .gravatar:hover{
1968 background-color: #FDF784 !important;
2043 background-color: #FDF784 !important;
1969 }
2044 }
1970 #quick_login{
2045 #quick_login{
1971 min-height: 80px;
2046 min-height: 80px;
1972 margin: 37px 0 0 -251px;
2047 margin: 37px 0 0 -251px;
1973 padding: 4px;
2048 padding: 4px;
1974 position: absolute;
2049 position: absolute;
1975 width: 278px;
2050 width: 278px;
1976 background-color: #003B76;
2051 background-color: #003B76;
1977 background-repeat: repeat-x;
2052 background-repeat: repeat-x;
1978 background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
2053 background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
1979 background-image: -moz-linear-gradient(top, #003b76, #00376e);
2054 background-image: -moz-linear-gradient(top, #003b76, #00376e);
1980 background-image: -ms-linear-gradient(top, #003b76, #00376e);
2055 background-image: -ms-linear-gradient(top, #003b76, #00376e);
1981 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76), color-stop(100%, #00376e) );
2056 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76), color-stop(100%, #00376e) );
1982 background-image: -webkit-linear-gradient(top, #003b76, #00376e);
2057 background-image: -webkit-linear-gradient(top, #003b76, #00376e);
1983 background-image: -o-linear-gradient(top, #003b76, #00376e);
2058 background-image: -o-linear-gradient(top, #003b76, #00376e);
1984 background-image: linear-gradient(top, #003b76, #00376e);
2059 background-image: linear-gradient(top, #003b76, #00376e);
1985 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76', endColorstr='#00376e', GradientType=0 );
2060 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76', endColorstr='#00376e', GradientType=0 );
1986
2061
1987 z-index: 999;
2062 z-index: 999;
1988 -webkit-border-radius: 0px 0px 4px 4px;
2063 -webkit-border-radius: 0px 0px 4px 4px;
1989 -khtml-border-radius: 0px 0px 4px 4px;
2064 -khtml-border-radius: 0px 0px 4px 4px;
1990 -moz-border-radius: 0px 0px 4px 4px;
2065 -moz-border-radius: 0px 0px 4px 4px;
1991 border-radius: 0px 0px 4px 4px;
2066 border-radius: 0px 0px 4px 4px;
1992 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
2067 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
1993 }
2068 }
1994 #quick_login h4{
2069 #quick_login h4{
1995 color: #fff;
2070 color: #fff;
1996 padding: 5px 0px 5px 14px;
2071 padding: 5px 0px 5px 14px;
1997 }
2072 }
1998
2073
1999 #quick_login .password_forgoten {
2074 #quick_login .password_forgoten {
2000 padding-right: 10px;
2075 padding-right: 10px;
2001 padding-top: 0px;
2076 padding-top: 0px;
2002 text-align: left;
2077 text-align: left;
2003 }
2078 }
2004
2079
2005 #quick_login .password_forgoten a {
2080 #quick_login .password_forgoten a {
2006 font-size: 10px;
2081 font-size: 10px;
2007 color: #fff;
2082 color: #fff;
2008 }
2083 }
2009
2084
2010 #quick_login .register {
2085 #quick_login .register {
2011 padding-right: 10px;
2086 padding-right: 10px;
2012 padding-top: 5px;
2087 padding-top: 5px;
2013 text-align: left;
2088 text-align: left;
2014 }
2089 }
2015
2090
2016 #quick_login .register a {
2091 #quick_login .register a {
2017 font-size: 10px;
2092 font-size: 10px;
2018 color: #fff;
2093 color: #fff;
2019 }
2094 }
2020
2095
2021 #quick_login .submit {
2096 #quick_login .submit {
2022 margin: -20px 0 0 0px;
2097 margin: -20px 0 0 0px;
2023 position: absolute;
2098 position: absolute;
2024 right: 15px;
2099 right: 15px;
2025 }
2100 }
2026
2101
2027 #quick_login .links_left{
2102 #quick_login .links_left{
2028 float: left;
2103 float: left;
2029 }
2104 }
2030 #quick_login .links_right{
2105 #quick_login .links_right{
2031 float: right;
2106 float: right;
2032 }
2107 }
2033 #quick_login .full_name{
2108 #quick_login .full_name{
2034 color: #FFFFFF;
2109 color: #FFFFFF;
2035 font-weight: bold;
2110 font-weight: bold;
2036 padding: 3px;
2111 padding: 3px;
2037 }
2112 }
2038 #quick_login .big_gravatar{
2113 #quick_login .big_gravatar{
2039 padding:4px 0px 0px 6px;
2114 padding:4px 0px 0px 6px;
2040 }
2115 }
2041 #quick_login .inbox{
2116 #quick_login .inbox{
2042 padding:4px 0px 0px 6px;
2117 padding:4px 0px 0px 6px;
2043 color: #FFFFFF;
2118 color: #FFFFFF;
2044 font-weight: bold;
2119 font-weight: bold;
2045 }
2120 }
2046 #quick_login .inbox a{
2121 #quick_login .inbox a{
2047 color: #FFFFFF;
2122 color: #FFFFFF;
2048 }
2123 }
2049 #quick_login .email,#quick_login .email a{
2124 #quick_login .email,#quick_login .email a{
2050 color: #FFFFFF;
2125 color: #FFFFFF;
2051 padding: 3px;
2126 padding: 3px;
2052
2127
2053 }
2128 }
2054 #quick_login .links .logout{
2129 #quick_login .links .logout{
2055
2130
2056 }
2131 }
2057
2132
2058 #quick_login div.form div.fields {
2133 #quick_login div.form div.fields {
2059 padding-top: 2px;
2134 padding-top: 2px;
2060 padding-left: 10px;
2135 padding-left: 10px;
2061 }
2136 }
2062
2137
2063 #quick_login div.form div.fields div.field {
2138 #quick_login div.form div.fields div.field {
2064 padding: 5px;
2139 padding: 5px;
2065 }
2140 }
2066
2141
2067 #quick_login div.form div.fields div.field div.label label {
2142 #quick_login div.form div.fields div.field div.label label {
2068 color: #fff;
2143 color: #fff;
2069 padding-bottom: 3px;
2144 padding-bottom: 3px;
2070 }
2145 }
2071
2146
2072 #quick_login div.form div.fields div.field div.input input {
2147 #quick_login div.form div.fields div.field div.input input {
2073 width: 236px;
2148 width: 236px;
2074 background: #FFF;
2149 background: #FFF;
2075 border-top: 1px solid #b3b3b3;
2150 border-top: 1px solid #b3b3b3;
2076 border-left: 1px solid #b3b3b3;
2151 border-left: 1px solid #b3b3b3;
2077 border-right: 1px solid #eaeaea;
2152 border-right: 1px solid #eaeaea;
2078 border-bottom: 1px solid #eaeaea;
2153 border-bottom: 1px solid #eaeaea;
2079 color: #000;
2154 color: #000;
2080 font-size: 11px;
2155 font-size: 11px;
2081 margin: 0;
2156 margin: 0;
2082 padding: 5px 7px 4px;
2157 padding: 5px 7px 4px;
2083 }
2158 }
2084
2159
2085 #quick_login div.form div.fields div.buttons {
2160 #quick_login div.form div.fields div.buttons {
2086 clear: both;
2161 clear: both;
2087 overflow: hidden;
2162 overflow: hidden;
2088 text-align: right;
2163 text-align: right;
2089 margin: 0;
2164 margin: 0;
2090 padding: 5px 14px 0px 5px;
2165 padding: 5px 14px 0px 5px;
2091 }
2166 }
2092
2167
2093 #quick_login div.form div.links {
2168 #quick_login div.form div.links {
2094 clear: both;
2169 clear: both;
2095 overflow: hidden;
2170 overflow: hidden;
2096 margin: 10px 0 0;
2171 margin: 10px 0 0;
2097 padding: 0 0 2px;
2172 padding: 0 0 2px;
2098 }
2173 }
2099
2174
2100 #quick_login ol.links{
2175 #quick_login ol.links{
2101 display: block;
2176 display: block;
2102 font-weight: bold;
2177 font-weight: bold;
2103 list-style: none outside none;
2178 list-style: none outside none;
2104 text-align: right;
2179 text-align: right;
2105 }
2180 }
2106 #quick_login ol.links li{
2181 #quick_login ol.links li{
2107 line-height: 27px;
2182 line-height: 27px;
2108 margin: 0;
2183 margin: 0;
2109 padding: 0;
2184 padding: 0;
2110 color: #fff;
2185 color: #fff;
2111 display: block;
2186 display: block;
2112 float:none !important;
2187 float:none !important;
2113 }
2188 }
2114
2189
2115 #quick_login ol.links li a{
2190 #quick_login ol.links li a{
2116 color: #fff;
2191 color: #fff;
2117 display: block;
2192 display: block;
2118 padding: 2px;
2193 padding: 2px;
2119 }
2194 }
2120 #quick_login ol.links li a:HOVER{
2195 #quick_login ol.links li a:HOVER{
2121 background-color: inherit !important;
2196 background-color: inherit !important;
2122 }
2197 }
2123
2198
2124 #register div.title {
2199 #register div.title {
2125 clear: both;
2200 clear: both;
2126 overflow: hidden;
2201 overflow: hidden;
2127 position: relative;
2202 position: relative;
2128 background-color: #003B76;
2203 background-color: #003B76;
2129 background-repeat: repeat-x;
2204 background-repeat: repeat-x;
2130 background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
2205 background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
2131 background-image: -moz-linear-gradient(top, #003b76, #00376e);
2206 background-image: -moz-linear-gradient(top, #003b76, #00376e);
2132 background-image: -ms-linear-gradient(top, #003b76, #00376e);
2207 background-image: -ms-linear-gradient(top, #003b76, #00376e);
2133 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76), color-stop(100%, #00376e) );
2208 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76), color-stop(100%, #00376e) );
2134 background-image: -webkit-linear-gradient(top, #003b76, #00376e);
2209 background-image: -webkit-linear-gradient(top, #003b76, #00376e);
2135 background-image: -o-linear-gradient(top, #003b76, #00376e);
2210 background-image: -o-linear-gradient(top, #003b76, #00376e);
2136 background-image: linear-gradient(top, #003b76, #00376e);
2211 background-image: linear-gradient(top, #003b76, #00376e);
2137 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',
2212 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',
2138 endColorstr='#00376e', GradientType=0 );
2213 endColorstr='#00376e', GradientType=0 );
2139 margin: 0 auto;
2214 margin: 0 auto;
2140 padding: 0;
2215 padding: 0;
2141 }
2216 }
2142
2217
2143 #register div.inner {
2218 #register div.inner {
2144 background: #FFF;
2219 background: #FFF;
2145 border-top: none;
2220 border-top: none;
2146 border-bottom: none;
2221 border-bottom: none;
2147 margin: 0 auto;
2222 margin: 0 auto;
2148 padding: 20px;
2223 padding: 20px;
2149 }
2224 }
2150
2225
2151 #register div.form div.fields div.field div.label {
2226 #register div.form div.fields div.field div.label {
2152 width: 135px;
2227 width: 135px;
2153 float: left;
2228 float: left;
2154 text-align: right;
2229 text-align: right;
2155 margin: 2px 10px 0 0;
2230 margin: 2px 10px 0 0;
2156 padding: 5px 0 0 5px;
2231 padding: 5px 0 0 5px;
2157 }
2232 }
2158
2233
2159 #register div.form div.fields div.field div.input input {
2234 #register div.form div.fields div.field div.input input {
2160 width: 300px;
2235 width: 300px;
2161 background: #FFF;
2236 background: #FFF;
2162 border-top: 1px solid #b3b3b3;
2237 border-top: 1px solid #b3b3b3;
2163 border-left: 1px solid #b3b3b3;
2238 border-left: 1px solid #b3b3b3;
2164 border-right: 1px solid #eaeaea;
2239 border-right: 1px solid #eaeaea;
2165 border-bottom: 1px solid #eaeaea;
2240 border-bottom: 1px solid #eaeaea;
2166 color: #000;
2241 color: #000;
2167 font-size: 11px;
2242 font-size: 11px;
2168 margin: 0;
2243 margin: 0;
2169 padding: 7px 7px 6px;
2244 padding: 7px 7px 6px;
2170 }
2245 }
2171
2246
2172 #register div.form div.fields div.buttons {
2247 #register div.form div.fields div.buttons {
2173 clear: both;
2248 clear: both;
2174 overflow: hidden;
2249 overflow: hidden;
2175 border-top: 1px solid #DDD;
2250 border-top: 1px solid #DDD;
2176 text-align: left;
2251 text-align: left;
2177 margin: 0;
2252 margin: 0;
2178 padding: 10px 0 0 150px;
2253 padding: 10px 0 0 150px;
2179 }
2254 }
2180
2255
2181 #register div.form div.activation_msg {
2256 #register div.form div.activation_msg {
2182 padding-top: 4px;
2257 padding-top: 4px;
2183 padding-bottom: 4px;
2258 padding-bottom: 4px;
2184 }
2259 }
2185
2260
2186 #journal .journal_day {
2261 #journal .journal_day {
2187 font-size: 20px;
2262 font-size: 20px;
2188 padding: 10px 0px;
2263 padding: 10px 0px;
2189 border-bottom: 2px solid #DDD;
2264 border-bottom: 2px solid #DDD;
2190 margin-left: 10px;
2265 margin-left: 10px;
2191 margin-right: 10px;
2266 margin-right: 10px;
2192 }
2267 }
2193
2268
2194 #journal .journal_container {
2269 #journal .journal_container {
2195 padding: 5px;
2270 padding: 5px;
2196 clear: both;
2271 clear: both;
2197 margin: 0px 5px 0px 10px;
2272 margin: 0px 5px 0px 10px;
2198 }
2273 }
2199
2274
2200 #journal .journal_action_container {
2275 #journal .journal_action_container {
2201 padding-left: 38px;
2276 padding-left: 38px;
2202 }
2277 }
2203
2278
2204 #journal .journal_user {
2279 #journal .journal_user {
2205 color: #747474;
2280 color: #747474;
2206 font-size: 14px;
2281 font-size: 14px;
2207 font-weight: bold;
2282 font-weight: bold;
2208 height: 30px;
2283 height: 30px;
2209 }
2284 }
2210
2285
2211 #journal .journal_icon {
2286 #journal .journal_icon {
2212 clear: both;
2287 clear: both;
2213 float: left;
2288 float: left;
2214 padding-right: 4px;
2289 padding-right: 4px;
2215 padding-top: 3px;
2290 padding-top: 3px;
2216 }
2291 }
2217
2292
2218 #journal .journal_action {
2293 #journal .journal_action {
2219 padding-top: 4px;
2294 padding-top: 4px;
2220 min-height: 2px;
2295 min-height: 2px;
2221 float: left
2296 float: left
2222 }
2297 }
2223
2298
2224 #journal .journal_action_params {
2299 #journal .journal_action_params {
2225 clear: left;
2300 clear: left;
2226 padding-left: 22px;
2301 padding-left: 22px;
2227 }
2302 }
2228
2303
2229 #journal .journal_repo {
2304 #journal .journal_repo {
2230 float: left;
2305 float: left;
2231 margin-left: 6px;
2306 margin-left: 6px;
2232 padding-top: 3px;
2307 padding-top: 3px;
2233 }
2308 }
2234
2309
2235 #journal .date {
2310 #journal .date {
2236 clear: both;
2311 clear: both;
2237 color: #777777;
2312 color: #777777;
2238 font-size: 11px;
2313 font-size: 11px;
2239 padding-left: 22px;
2314 padding-left: 22px;
2240 }
2315 }
2241
2316
2242 #journal .journal_repo .journal_repo_name {
2317 #journal .journal_repo .journal_repo_name {
2243 font-weight: bold;
2318 font-weight: bold;
2244 font-size: 1.1em;
2319 font-size: 1.1em;
2245 }
2320 }
2246
2321
2247 #journal .compare_view {
2322 #journal .compare_view {
2248 padding: 5px 0px 5px 0px;
2323 padding: 5px 0px 5px 0px;
2249 width: 95px;
2324 width: 95px;
2250 }
2325 }
2251
2326
2252 .journal_highlight {
2327 .journal_highlight {
2253 font-weight: bold;
2328 font-weight: bold;
2254 padding: 0 2px;
2329 padding: 0 2px;
2255 vertical-align: bottom;
2330 vertical-align: bottom;
2256 }
2331 }
2257
2332
2258 .trending_language_tbl,.trending_language_tbl td {
2333 .trending_language_tbl,.trending_language_tbl td {
2259 border: 0 !important;
2334 border: 0 !important;
2260 margin: 0 !important;
2335 margin: 0 !important;
2261 padding: 0 !important;
2336 padding: 0 !important;
2262 }
2337 }
2263
2338
2264 .trending_language_tbl,.trending_language_tbl tr {
2339 .trending_language_tbl,.trending_language_tbl tr {
2265 border-spacing: 1px;
2340 border-spacing: 1px;
2266 }
2341 }
2267
2342
2268 .trending_language {
2343 .trending_language {
2269 background-color: #003367;
2344 background-color: #003367;
2270 color: #FFF;
2345 color: #FFF;
2271 display: block;
2346 display: block;
2272 min-width: 20px;
2347 min-width: 20px;
2273 text-decoration: none;
2348 text-decoration: none;
2274 height: 12px;
2349 height: 12px;
2275 margin-bottom: 0px;
2350 margin-bottom: 0px;
2276 margin-left: 5px;
2351 margin-left: 5px;
2277 white-space: pre;
2352 white-space: pre;
2278 padding: 3px;
2353 padding: 3px;
2279 }
2354 }
2280
2355
2281 h3.files_location {
2356 h3.files_location {
2282 font-size: 1.8em;
2357 font-size: 1.8em;
2283 font-weight: 700;
2358 font-weight: 700;
2284 border-bottom: none !important;
2359 border-bottom: none !important;
2285 margin: 10px 0 !important;
2360 margin: 10px 0 !important;
2286 }
2361 }
2287
2362
2288 #files_data dl dt {
2363 #files_data dl dt {
2289 float: left;
2364 float: left;
2290 width: 60px;
2365 width: 60px;
2291 margin: 0 !important;
2366 margin: 0 !important;
2292 padding: 5px;
2367 padding: 5px;
2293 }
2368 }
2294
2369
2295 #files_data dl dd {
2370 #files_data dl dd {
2296 margin: 0 !important;
2371 margin: 0 !important;
2297 padding: 5px !important;
2372 padding: 5px !important;
2298 }
2373 }
2299
2374
2300 .file_history{
2375 .file_history{
2301 padding-top:10px;
2376 padding-top:10px;
2302 font-size:16px;
2377 font-size:16px;
2303 }
2378 }
2304 .file_author{
2379 .file_author{
2305 float: left;
2380 float: left;
2306 }
2381 }
2307
2382
2308 .file_author .item{
2383 .file_author .item{
2309 float:left;
2384 float:left;
2310 padding:5px;
2385 padding:5px;
2311 color: #888;
2386 color: #888;
2312 }
2387 }
2313
2388
2314 .tablerow0 {
2389 .tablerow0 {
2315 background-color: #F8F8F8;
2390 background-color: #F8F8F8;
2316 }
2391 }
2317
2392
2318 .tablerow1 {
2393 .tablerow1 {
2319 background-color: #FFFFFF;
2394 background-color: #FFFFFF;
2320 }
2395 }
2321
2396
2322 .changeset_id {
2397 .changeset_id {
2323 font-family: monospace;
2398 font-family: monospace;
2324 color: #666666;
2399 color: #666666;
2325 }
2400 }
2326
2401
2327 .changeset_hash {
2402 .changeset_hash {
2328 color: #000000;
2403 color: #000000;
2329 }
2404 }
2330
2405
2331 #changeset_content {
2406 #changeset_content {
2332 border-left: 1px solid #CCC;
2407 border-left: 1px solid #CCC;
2333 border-right: 1px solid #CCC;
2408 border-right: 1px solid #CCC;
2334 border-bottom: 1px solid #CCC;
2409 border-bottom: 1px solid #CCC;
2335 padding: 5px;
2410 padding: 5px;
2336 }
2411 }
2337
2412
2338 #changeset_compare_view_content {
2413 #changeset_compare_view_content {
2339 border: 1px solid #CCC;
2414 border: 1px solid #CCC;
2340 padding: 5px;
2415 padding: 5px;
2341 }
2416 }
2342
2417
2343 #changeset_content .container {
2418 #changeset_content .container {
2344 min-height: 100px;
2419 min-height: 100px;
2345 font-size: 1.2em;
2420 font-size: 1.2em;
2346 overflow: hidden;
2421 overflow: hidden;
2347 }
2422 }
2348
2423
2349 #changeset_compare_view_content .compare_view_commits {
2424 #changeset_compare_view_content .compare_view_commits {
2350 width: auto !important;
2425 width: auto !important;
2351 }
2426 }
2352
2427
2353 #changeset_compare_view_content .compare_view_commits td {
2428 #changeset_compare_view_content .compare_view_commits td {
2354 padding: 0px 0px 0px 12px !important;
2429 padding: 0px 0px 0px 12px !important;
2355 }
2430 }
2356
2431
2357 #changeset_content .container .right {
2432 #changeset_content .container .right {
2358 float: right;
2433 float: right;
2359 width: 20%;
2434 width: 20%;
2360 text-align: right;
2435 text-align: right;
2361 }
2436 }
2362
2437
2363 #changeset_content .container .left .message {
2438 #changeset_content .container .left .message {
2364 white-space: pre-wrap;
2439 white-space: pre-wrap;
2365 }
2440 }
2366 #changeset_content .container .left .message a:hover {
2441 #changeset_content .container .left .message a:hover {
2367 text-decoration: none;
2442 text-decoration: none;
2368 }
2443 }
2369 .cs_files .cur_cs {
2444 .cs_files .cur_cs {
2370 margin: 10px 2px;
2445 margin: 10px 2px;
2371 font-weight: bold;
2446 font-weight: bold;
2372 }
2447 }
2373
2448
2374 .cs_files .node {
2449 .cs_files .node {
2375 float: left;
2450 float: left;
2376 }
2451 }
2377
2452
2378 .cs_files .changes {
2453 .cs_files .changes {
2379 float: right;
2454 float: right;
2380 color:#003367;
2455 color:#003367;
2381
2456
2382 }
2457 }
2383
2458
2384 .cs_files .changes .added {
2459 .cs_files .changes .added {
2385 background-color: #BBFFBB;
2460 background-color: #BBFFBB;
2386 float: left;
2461 float: left;
2387 text-align: center;
2462 text-align: center;
2388 font-size: 9px;
2463 font-size: 9px;
2389 padding: 2px 0px 2px 0px;
2464 padding: 2px 0px 2px 0px;
2390 }
2465 }
2391
2466
2392 .cs_files .changes .deleted {
2467 .cs_files .changes .deleted {
2393 background-color: #FF8888;
2468 background-color: #FF8888;
2394 float: left;
2469 float: left;
2395 text-align: center;
2470 text-align: center;
2396 font-size: 9px;
2471 font-size: 9px;
2397 padding: 2px 0px 2px 0px;
2472 padding: 2px 0px 2px 0px;
2398 }
2473 }
2399
2474
2400 .cs_files .cs_added,.cs_files .cs_A {
2475 .cs_files .cs_added,.cs_files .cs_A {
2401 background: url("../images/icons/page_white_add.png") no-repeat scroll
2476 background: url("../images/icons/page_white_add.png") no-repeat scroll
2402 3px;
2477 3px;
2403 height: 16px;
2478 height: 16px;
2404 padding-left: 20px;
2479 padding-left: 20px;
2405 margin-top: 7px;
2480 margin-top: 7px;
2406 text-align: left;
2481 text-align: left;
2407 }
2482 }
2408
2483
2409 .cs_files .cs_changed,.cs_files .cs_M {
2484 .cs_files .cs_changed,.cs_files .cs_M {
2410 background: url("../images/icons/page_white_edit.png") no-repeat scroll
2485 background: url("../images/icons/page_white_edit.png") no-repeat scroll
2411 3px;
2486 3px;
2412 height: 16px;
2487 height: 16px;
2413 padding-left: 20px;
2488 padding-left: 20px;
2414 margin-top: 7px;
2489 margin-top: 7px;
2415 text-align: left;
2490 text-align: left;
2416 }
2491 }
2417
2492
2418 .cs_files .cs_removed,.cs_files .cs_D {
2493 .cs_files .cs_removed,.cs_files .cs_D {
2419 background: url("../images/icons/page_white_delete.png") no-repeat
2494 background: url("../images/icons/page_white_delete.png") no-repeat
2420 scroll 3px;
2495 scroll 3px;
2421 height: 16px;
2496 height: 16px;
2422 padding-left: 20px;
2497 padding-left: 20px;
2423 margin-top: 7px;
2498 margin-top: 7px;
2424 text-align: left;
2499 text-align: left;
2425 }
2500 }
2426
2501
2427 #graph {
2502 #graph {
2428 overflow: hidden;
2503 overflow: hidden;
2429 }
2504 }
2430
2505
2431 #graph_nodes {
2506 #graph_nodes {
2432 float: left;
2507 float: left;
2433 margin-right: -6px;
2508 margin-right: -6px;
2434 margin-top: 0px;
2509 margin-top: 0px;
2435 }
2510 }
2436
2511
2437 #graph_content {
2512 #graph_content {
2438 width: 80%;
2513 width: 80%;
2439 float: left;
2514 float: left;
2440 }
2515 }
2441
2516
2442 #graph_content .container_header {
2517 #graph_content .container_header {
2443 border-bottom: 1px solid #DDD;
2518 border-bottom: 1px solid #DDD;
2444 padding: 10px;
2519 padding: 10px;
2445 height: 25px;
2520 height: 25px;
2446 }
2521 }
2447
2522
2448 #graph_content #rev_range_container {
2523 #graph_content #rev_range_container {
2449 padding: 7px 20px;
2524 padding: 7px 20px;
2450 float: left;
2525 float: left;
2451 }
2526 }
2452
2527
2453 #graph_content .container {
2528 #graph_content .container {
2454 border-bottom: 1px solid #DDD;
2529 border-bottom: 1px solid #DDD;
2455 height: 56px;
2530 height: 56px;
2456 overflow: hidden;
2531 overflow: hidden;
2457 }
2532 }
2458
2533
2459 #graph_content .container .right {
2534 #graph_content .container .right {
2460 float: right;
2535 float: right;
2461 width: 23%;
2536 width: 23%;
2462 text-align: right;
2537 text-align: right;
2463 }
2538 }
2464
2539
2465 #graph_content .container .left {
2540 #graph_content .container .left {
2466 float: left;
2541 float: left;
2467 width: 25%;
2542 width: 25%;
2468 padding-left: 5px;
2543 padding-left: 5px;
2469 }
2544 }
2470
2545
2471 #graph_content .container .mid {
2546 #graph_content .container .mid {
2472 float: left;
2547 float: left;
2473 width: 49%;
2548 width: 49%;
2474 }
2549 }
2475
2550
2476
2551
2477 #graph_content .container .left .date {
2552 #graph_content .container .left .date {
2478 color: #666;
2553 color: #666;
2479 padding-left: 22px;
2554 padding-left: 22px;
2480 font-size: 10px;
2555 font-size: 10px;
2481 }
2556 }
2482
2557
2483 #graph_content .container .left .author {
2558 #graph_content .container .left .author {
2484 height: 22px;
2559 height: 22px;
2485 }
2560 }
2486
2561
2487 #graph_content .container .left .author .user {
2562 #graph_content .container .left .author .user {
2488 color: #444444;
2563 color: #444444;
2489 float: left;
2564 float: left;
2490 margin-left: -4px;
2565 margin-left: -4px;
2491 margin-top: 4px;
2566 margin-top: 4px;
2492 }
2567 }
2493
2568
2494 #graph_content .container .mid .message {
2569 #graph_content .container .mid .message {
2495 white-space: pre-wrap;
2570 white-space: pre-wrap;
2496 }
2571 }
2497
2572
2498 #graph_content .container .mid .message a:hover{
2573 #graph_content .container .mid .message a:hover{
2499 text-decoration: none;
2574 text-decoration: none;
2500 }
2575 }
2501 #content #graph_content .message .revision-link,
2576 #content #graph_content .message .revision-link,
2502 #changeset_content .container .message .revision-link
2577 #changeset_content .container .message .revision-link
2503 {
2578 {
2504 color:#3F6F9F;
2579 color:#3F6F9F;
2505 font-weight: bold !important;
2580 font-weight: bold !important;
2506 }
2581 }
2507
2582
2508 #content #graph_content .message .issue-tracker-link,
2583 #content #graph_content .message .issue-tracker-link,
2509 #changeset_content .container .message .issue-tracker-link{
2584 #changeset_content .container .message .issue-tracker-link{
2510 color:#3F6F9F;
2585 color:#3F6F9F;
2511 font-weight: bold !important;
2586 font-weight: bold !important;
2512 }
2587 }
2513
2588
2514 .changeset-status-container{
2589 .changeset-status-container{
2515 padding-right: 5px;
2590 padding-right: 5px;
2516 margin-top:1px;
2591 margin-top:1px;
2517 float:right;
2592 float:right;
2518 height:14px;
2593 height:14px;
2519 }
2594 }
2520 .code-header .changeset-status-container{
2595 .code-header .changeset-status-container{
2521 float:left;
2596 float:left;
2522 padding:2px 0px 0px 2px;
2597 padding:2px 0px 0px 2px;
2523 }
2598 }
2524 .changeset-status-container .changeset-status-lbl{
2599 .changeset-status-container .changeset-status-lbl{
2525 color: rgb(136, 136, 136);
2600 color: rgb(136, 136, 136);
2526 float: left;
2601 float: left;
2527 padding: 3px 4px 0px 0px
2602 padding: 3px 4px 0px 0px
2528 }
2603 }
2529 .code-header .changeset-status-container .changeset-status-lbl{
2604 .code-header .changeset-status-container .changeset-status-lbl{
2530 float: left;
2605 float: left;
2531 padding: 0px 4px 0px 0px;
2606 padding: 0px 4px 0px 0px;
2532 }
2607 }
2533 .changeset-status-container .changeset-status-ico{
2608 .changeset-status-container .changeset-status-ico{
2534 float: left;
2609 float: left;
2535 }
2610 }
2536 .code-header .changeset-status-container .changeset-status-ico, .container .changeset-status-ico{
2611 .code-header .changeset-status-container .changeset-status-ico, .container .changeset-status-ico{
2537 float: left;
2612 float: left;
2538 }
2613 }
2539 .right .comments-container{
2614 .right .comments-container{
2540 padding-right: 5px;
2615 padding-right: 5px;
2541 margin-top:1px;
2616 margin-top:1px;
2542 float:right;
2617 float:right;
2543 height:14px;
2618 height:14px;
2544 }
2619 }
2545
2620
2546 .right .comments-cnt{
2621 .right .comments-cnt{
2547 float: left;
2622 float: left;
2548 color: rgb(136, 136, 136);
2623 color: rgb(136, 136, 136);
2549 padding-right: 2px;
2624 padding-right: 2px;
2550 }
2625 }
2551
2626
2552 .right .changes{
2627 .right .changes{
2553 clear: both;
2628 clear: both;
2554 }
2629 }
2555
2630
2556 .right .changes .changed_total {
2631 .right .changes .changed_total {
2557 display: block;
2632 display: block;
2558 float: right;
2633 float: right;
2559 text-align: center;
2634 text-align: center;
2560 min-width: 45px;
2635 min-width: 45px;
2561 cursor: pointer;
2636 cursor: pointer;
2562 color: #444444;
2637 color: #444444;
2563 background: #FEA;
2638 background: #FEA;
2564 -webkit-border-radius: 0px 0px 0px 6px;
2639 -webkit-border-radius: 0px 0px 0px 6px;
2565 -moz-border-radius: 0px 0px 0px 6px;
2640 -moz-border-radius: 0px 0px 0px 6px;
2566 border-radius: 0px 0px 0px 6px;
2641 border-radius: 0px 0px 0px 6px;
2567 padding: 1px;
2642 padding: 1px;
2568 }
2643 }
2569
2644
2570 .right .changes .added,.changed,.removed {
2645 .right .changes .added,.changed,.removed {
2571 display: block;
2646 display: block;
2572 padding: 1px;
2647 padding: 1px;
2573 color: #444444;
2648 color: #444444;
2574 float: right;
2649 float: right;
2575 text-align: center;
2650 text-align: center;
2576 min-width: 15px;
2651 min-width: 15px;
2577 }
2652 }
2578
2653
2579 .right .changes .added {
2654 .right .changes .added {
2580 background: #CFC;
2655 background: #CFC;
2581 }
2656 }
2582
2657
2583 .right .changes .changed {
2658 .right .changes .changed {
2584 background: #FEA;
2659 background: #FEA;
2585 }
2660 }
2586
2661
2587 .right .changes .removed {
2662 .right .changes .removed {
2588 background: #FAA;
2663 background: #FAA;
2589 }
2664 }
2590
2665
2591 .right .merge {
2666 .right .merge {
2592 padding: 1px 3px 1px 3px;
2667 padding: 1px 3px 1px 3px;
2593 background-color: #fca062;
2668 background-color: #fca062;
2594 font-size: 10px;
2669 font-size: 10px;
2595 font-weight: bold;
2670 font-weight: bold;
2596 color: #ffffff;
2671 color: #ffffff;
2597 text-transform: uppercase;
2672 text-transform: uppercase;
2598 white-space: nowrap;
2673 white-space: nowrap;
2599 -webkit-border-radius: 3px;
2674 -webkit-border-radius: 3px;
2600 -moz-border-radius: 3px;
2675 -moz-border-radius: 3px;
2601 border-radius: 3px;
2676 border-radius: 3px;
2602 margin-right: 2px;
2677 margin-right: 2px;
2603 }
2678 }
2604
2679
2605 .right .parent {
2680 .right .parent {
2606 color: #666666;
2681 color: #666666;
2607 clear:both;
2682 clear:both;
2608 }
2683 }
2609 .right .logtags{
2684 .right .logtags{
2610 padding: 2px 2px 2px 2px;
2685 padding: 2px 2px 2px 2px;
2611 }
2686 }
2612 .right .logtags .branchtag,.right .logtags .tagtag,.right .logtags .booktag{
2687 .right .logtags .branchtag,.right .logtags .tagtag,.right .logtags .booktag{
2613 margin: 0px 2px;
2688 margin: 0px 2px;
2614 }
2689 }
2615
2690
2616 .right .logtags .branchtag,.logtags .branchtag {
2691 .right .logtags .branchtag,.logtags .branchtag {
2617 padding: 1px 3px 1px 3px;
2692 padding: 1px 3px 1px 3px;
2618 background-color: #bfbfbf;
2693 background-color: #bfbfbf;
2619 font-size: 10px;
2694 font-size: 10px;
2620 font-weight: bold;
2695 font-weight: bold;
2621 color: #ffffff;
2696 color: #ffffff;
2622 text-transform: uppercase;
2697 text-transform: uppercase;
2623 white-space: nowrap;
2698 white-space: nowrap;
2624 -webkit-border-radius: 3px;
2699 -webkit-border-radius: 3px;
2625 -moz-border-radius: 3px;
2700 -moz-border-radius: 3px;
2626 border-radius: 3px;
2701 border-radius: 3px;
2627 }
2702 }
2628 .right .logtags .branchtag a:hover,.logtags .branchtag a{
2703 .right .logtags .branchtag a:hover,.logtags .branchtag a{
2629 color: #ffffff;
2704 color: #ffffff;
2630 }
2705 }
2631 .right .logtags .branchtag a:hover,.logtags .branchtag a:hover{
2706 .right .logtags .branchtag a:hover,.logtags .branchtag a:hover{
2632 text-decoration: none;
2707 text-decoration: none;
2633 color: #ffffff;
2708 color: #ffffff;
2634 }
2709 }
2635 .right .logtags .tagtag,.logtags .tagtag {
2710 .right .logtags .tagtag,.logtags .tagtag {
2636 padding: 1px 3px 1px 3px;
2711 padding: 1px 3px 1px 3px;
2637 background-color: #62cffc;
2712 background-color: #62cffc;
2638 font-size: 10px;
2713 font-size: 10px;
2639 font-weight: bold;
2714 font-weight: bold;
2640 color: #ffffff;
2715 color: #ffffff;
2641 text-transform: uppercase;
2716 text-transform: uppercase;
2642 white-space: nowrap;
2717 white-space: nowrap;
2643 -webkit-border-radius: 3px;
2718 -webkit-border-radius: 3px;
2644 -moz-border-radius: 3px;
2719 -moz-border-radius: 3px;
2645 border-radius: 3px;
2720 border-radius: 3px;
2646 }
2721 }
2647 .right .logtags .tagtag a:hover,.logtags .tagtag a{
2722 .right .logtags .tagtag a:hover,.logtags .tagtag a{
2648 color: #ffffff;
2723 color: #ffffff;
2649 }
2724 }
2650 .right .logtags .tagtag a:hover,.logtags .tagtag a:hover{
2725 .right .logtags .tagtag a:hover,.logtags .tagtag a:hover{
2651 text-decoration: none;
2726 text-decoration: none;
2652 color: #ffffff;
2727 color: #ffffff;
2653 }
2728 }
2654 .right .logbooks .bookbook,.logbooks .bookbook,.right .logtags .bookbook,.logtags .bookbook {
2729 .right .logbooks .bookbook,.logbooks .bookbook,.right .logtags .bookbook,.logtags .bookbook {
2655 padding: 1px 3px 1px 3px;
2730 padding: 1px 3px 1px 3px;
2656 background-color: #46A546;
2731 background-color: #46A546;
2657 font-size: 10px;
2732 font-size: 10px;
2658 font-weight: bold;
2733 font-weight: bold;
2659 color: #ffffff;
2734 color: #ffffff;
2660 text-transform: uppercase;
2735 text-transform: uppercase;
2661 white-space: nowrap;
2736 white-space: nowrap;
2662 -webkit-border-radius: 3px;
2737 -webkit-border-radius: 3px;
2663 -moz-border-radius: 3px;
2738 -moz-border-radius: 3px;
2664 border-radius: 3px;
2739 border-radius: 3px;
2665 }
2740 }
2666 .right .logbooks .bookbook,.logbooks .bookbook a,.right .logtags .bookbook,.logtags .bookbook a{
2741 .right .logbooks .bookbook,.logbooks .bookbook a,.right .logtags .bookbook,.logtags .bookbook a{
2667 color: #ffffff;
2742 color: #ffffff;
2668 }
2743 }
2669 .right .logbooks .bookbook,.logbooks .bookbook a:hover,.right .logtags .bookbook,.logtags .bookbook a:hover{
2744 .right .logbooks .bookbook,.logbooks .bookbook a:hover,.right .logtags .bookbook,.logtags .bookbook a:hover{
2670 text-decoration: none;
2745 text-decoration: none;
2671 color: #ffffff;
2746 color: #ffffff;
2672 }
2747 }
2673 div.browserblock {
2748 div.browserblock {
2674 overflow: hidden;
2749 overflow: hidden;
2675 border: 1px solid #ccc;
2750 border: 1px solid #ccc;
2676 background: #f8f8f8;
2751 background: #f8f8f8;
2677 font-size: 100%;
2752 font-size: 100%;
2678 line-height: 125%;
2753 line-height: 125%;
2679 padding: 0;
2754 padding: 0;
2680 -webkit-border-radius: 6px 6px 0px 0px;
2755 -webkit-border-radius: 6px 6px 0px 0px;
2681 -moz-border-radius: 6px 6px 0px 0px;
2756 -moz-border-radius: 6px 6px 0px 0px;
2682 border-radius: 6px 6px 0px 0px;
2757 border-radius: 6px 6px 0px 0px;
2683 }
2758 }
2684
2759
2685 div.browserblock .browser-header {
2760 div.browserblock .browser-header {
2686 background: #FFF;
2761 background: #FFF;
2687 padding: 10px 0px 15px 0px;
2762 padding: 10px 0px 15px 0px;
2688 width: 100%;
2763 width: 100%;
2689 }
2764 }
2690
2765
2691 div.browserblock .browser-nav {
2766 div.browserblock .browser-nav {
2692 float: left
2767 float: left
2693 }
2768 }
2694
2769
2695 div.browserblock .browser-branch {
2770 div.browserblock .browser-branch {
2696 float: left;
2771 float: left;
2697 }
2772 }
2698
2773
2699 div.browserblock .browser-branch label {
2774 div.browserblock .browser-branch label {
2700 color: #4A4A4A;
2775 color: #4A4A4A;
2701 vertical-align: text-top;
2776 vertical-align: text-top;
2702 }
2777 }
2703
2778
2704 div.browserblock .browser-header span {
2779 div.browserblock .browser-header span {
2705 margin-left: 5px;
2780 margin-left: 5px;
2706 font-weight: 700;
2781 font-weight: 700;
2707 }
2782 }
2708
2783
2709 div.browserblock .browser-search {
2784 div.browserblock .browser-search {
2710 clear: both;
2785 clear: both;
2711 padding: 8px 8px 0px 5px;
2786 padding: 8px 8px 0px 5px;
2712 height: 20px;
2787 height: 20px;
2713 }
2788 }
2714
2789
2715 div.browserblock #node_filter_box {
2790 div.browserblock #node_filter_box {
2716
2791
2717 }
2792 }
2718
2793
2719 div.browserblock .search_activate {
2794 div.browserblock .search_activate {
2720 float: left
2795 float: left
2721 }
2796 }
2722
2797
2723 div.browserblock .add_node {
2798 div.browserblock .add_node {
2724 float: left;
2799 float: left;
2725 padding-left: 5px;
2800 padding-left: 5px;
2726 }
2801 }
2727
2802
2728 div.browserblock .search_activate a:hover,div.browserblock .add_node a:hover
2803 div.browserblock .search_activate a:hover,div.browserblock .add_node a:hover
2729 {
2804 {
2730 text-decoration: none !important;
2805 text-decoration: none !important;
2731 }
2806 }
2732
2807
2733 div.browserblock .browser-body {
2808 div.browserblock .browser-body {
2734 background: #EEE;
2809 background: #EEE;
2735 border-top: 1px solid #CCC;
2810 border-top: 1px solid #CCC;
2736 }
2811 }
2737
2812
2738 table.code-browser {
2813 table.code-browser {
2739 border-collapse: collapse;
2814 border-collapse: collapse;
2740 width: 100%;
2815 width: 100%;
2741 }
2816 }
2742
2817
2743 table.code-browser tr {
2818 table.code-browser tr {
2744 margin: 3px;
2819 margin: 3px;
2745 }
2820 }
2746
2821
2747 table.code-browser thead th {
2822 table.code-browser thead th {
2748 background-color: #EEE;
2823 background-color: #EEE;
2749 height: 20px;
2824 height: 20px;
2750 font-size: 1.1em;
2825 font-size: 1.1em;
2751 font-weight: 700;
2826 font-weight: 700;
2752 text-align: left;
2827 text-align: left;
2753 padding-left: 10px;
2828 padding-left: 10px;
2754 }
2829 }
2755
2830
2756 table.code-browser tbody td {
2831 table.code-browser tbody td {
2757 padding-left: 10px;
2832 padding-left: 10px;
2758 height: 20px;
2833 height: 20px;
2759 }
2834 }
2760
2835
2761 table.code-browser .browser-file {
2836 table.code-browser .browser-file {
2762 background: url("../images/icons/document_16.png") no-repeat scroll 3px;
2837 background: url("../images/icons/document_16.png") no-repeat scroll 3px;
2763 height: 16px;
2838 height: 16px;
2764 padding-left: 20px;
2839 padding-left: 20px;
2765 text-align: left;
2840 text-align: left;
2766 }
2841 }
2767 .diffblock .changeset_header {
2842 .diffblock .changeset_header {
2768 height: 16px;
2843 height: 16px;
2769 }
2844 }
2770 .diffblock .changeset_file {
2845 .diffblock .changeset_file {
2771 background: url("../images/icons/file.png") no-repeat scroll 3px;
2846 background: url("../images/icons/file.png") no-repeat scroll 3px;
2772 text-align: left;
2847 text-align: left;
2773 float: left;
2848 float: left;
2774 padding: 2px 0px 2px 22px;
2849 padding: 2px 0px 2px 22px;
2775 }
2850 }
2776 .diffblock .diff-menu-wrapper{
2851 .diffblock .diff-menu-wrapper{
2777 float: left;
2852 float: left;
2778 }
2853 }
2779
2854
2780 .diffblock .diff-menu{
2855 .diffblock .diff-menu{
2781 position: absolute;
2856 position: absolute;
2782 background: none repeat scroll 0 0 #FFFFFF;
2857 background: none repeat scroll 0 0 #FFFFFF;
2783 border-color: #003367 #666666 #666666;
2858 border-color: #003367 #666666 #666666;
2784 border-right: 1px solid #666666;
2859 border-right: 1px solid #666666;
2785 border-style: solid solid solid;
2860 border-style: solid solid solid;
2786 border-width: 1px;
2861 border-width: 1px;
2787 box-shadow: 2px 8px 4px rgba(0, 0, 0, 0.2);
2862 box-shadow: 2px 8px 4px rgba(0, 0, 0, 0.2);
2788 margin-top:5px;
2863 margin-top:5px;
2789 margin-left:1px;
2864 margin-left:1px;
2790
2865
2791 }
2866 }
2792 .diffblock .diff-actions {
2867 .diffblock .diff-actions {
2793 padding: 2px 0px 0px 2px;
2868 padding: 2px 0px 0px 2px;
2794 float: left;
2869 float: left;
2795 }
2870 }
2796 .diffblock .diff-menu ul li {
2871 .diffblock .diff-menu ul li {
2797 padding: 0px 0px 0px 0px !important;
2872 padding: 0px 0px 0px 0px !important;
2798 }
2873 }
2799 .diffblock .diff-menu ul li a{
2874 .diffblock .diff-menu ul li a{
2800 display: block;
2875 display: block;
2801 padding: 3px 8px 3px 8px !important;
2876 padding: 3px 8px 3px 8px !important;
2802 }
2877 }
2803 .diffblock .diff-menu ul li a:hover{
2878 .diffblock .diff-menu ul li a:hover{
2804 text-decoration: none;
2879 text-decoration: none;
2805 background-color: #EEEEEE;
2880 background-color: #EEEEEE;
2806 }
2881 }
2807 table.code-browser .browser-dir {
2882 table.code-browser .browser-dir {
2808 background: url("../images/icons/folder_16.png") no-repeat scroll 3px;
2883 background: url("../images/icons/folder_16.png") no-repeat scroll 3px;
2809 height: 16px;
2884 height: 16px;
2810 padding-left: 20px;
2885 padding-left: 20px;
2811 text-align: left;
2886 text-align: left;
2812 }
2887 }
2813
2888
2814 table.code-browser .submodule-dir {
2889 table.code-browser .submodule-dir {
2815 background: url("../images/icons/disconnect.png") no-repeat scroll 3px;
2890 background: url("../images/icons/disconnect.png") no-repeat scroll 3px;
2816 height: 16px;
2891 height: 16px;
2817 padding-left: 20px;
2892 padding-left: 20px;
2818 text-align: left;
2893 text-align: left;
2819 }
2894 }
2820
2895
2821
2896
2822 .box .search {
2897 .box .search {
2823 clear: both;
2898 clear: both;
2824 overflow: hidden;
2899 overflow: hidden;
2825 margin: 0;
2900 margin: 0;
2826 padding: 0 20px 10px;
2901 padding: 0 20px 10px;
2827 }
2902 }
2828
2903
2829 .box .search div.search_path {
2904 .box .search div.search_path {
2830 background: none repeat scroll 0 0 #EEE;
2905 background: none repeat scroll 0 0 #EEE;
2831 border: 1px solid #CCC;
2906 border: 1px solid #CCC;
2832 color: blue;
2907 color: blue;
2833 margin-bottom: 10px;
2908 margin-bottom: 10px;
2834 padding: 10px 0;
2909 padding: 10px 0;
2835 }
2910 }
2836
2911
2837 .box .search div.search_path div.link {
2912 .box .search div.search_path div.link {
2838 font-weight: 700;
2913 font-weight: 700;
2839 margin-left: 25px;
2914 margin-left: 25px;
2840 }
2915 }
2841
2916
2842 .box .search div.search_path div.link a {
2917 .box .search div.search_path div.link a {
2843 color: #003367;
2918 color: #003367;
2844 cursor: pointer;
2919 cursor: pointer;
2845 text-decoration: none;
2920 text-decoration: none;
2846 }
2921 }
2847
2922
2848 #path_unlock {
2923 #path_unlock {
2849 color: red;
2924 color: red;
2850 font-size: 1.2em;
2925 font-size: 1.2em;
2851 padding-left: 4px;
2926 padding-left: 4px;
2852 }
2927 }
2853
2928
2854 .info_box span {
2929 .info_box span {
2855 margin-left: 3px;
2930 margin-left: 3px;
2856 margin-right: 3px;
2931 margin-right: 3px;
2857 }
2932 }
2858
2933
2859 .info_box .rev {
2934 .info_box .rev {
2860 color: #003367;
2935 color: #003367;
2861 font-size: 1.6em;
2936 font-size: 1.6em;
2862 font-weight: bold;
2937 font-weight: bold;
2863 vertical-align: sub;
2938 vertical-align: sub;
2864 }
2939 }
2865
2940
2866 .info_box input#at_rev,.info_box input#size {
2941 .info_box input#at_rev,.info_box input#size {
2867 background: #FFF;
2942 background: #FFF;
2868 border-top: 1px solid #b3b3b3;
2943 border-top: 1px solid #b3b3b3;
2869 border-left: 1px solid #b3b3b3;
2944 border-left: 1px solid #b3b3b3;
2870 border-right: 1px solid #eaeaea;
2945 border-right: 1px solid #eaeaea;
2871 border-bottom: 1px solid #eaeaea;
2946 border-bottom: 1px solid #eaeaea;
2872 color: #000;
2947 color: #000;
2873 font-size: 12px;
2948 font-size: 12px;
2874 margin: 0;
2949 margin: 0;
2875 padding: 1px 5px 1px;
2950 padding: 1px 5px 1px;
2876 }
2951 }
2877
2952
2878 .info_box input#view {
2953 .info_box input#view {
2879 text-align: center;
2954 text-align: center;
2880 padding: 4px 3px 2px 2px;
2955 padding: 4px 3px 2px 2px;
2881 }
2956 }
2882
2957
2883 .yui-overlay,.yui-panel-container {
2958 .yui-overlay,.yui-panel-container {
2884 visibility: hidden;
2959 visibility: hidden;
2885 position: absolute;
2960 position: absolute;
2886 z-index: 2;
2961 z-index: 2;
2887 }
2962 }
2888
2963
2889 .yui-tt {
2964 .yui-tt {
2890 visibility: hidden;
2965 visibility: hidden;
2891 position: absolute;
2966 position: absolute;
2892 color: #666;
2967 color: #666;
2893 background-color: #FFF;
2968 background-color: #FFF;
2894 border: 2px solid #003367;
2969 border: 2px solid #003367;
2895 font: 100% sans-serif;
2970 font: 100% sans-serif;
2896 width: auto;
2971 width: auto;
2897 opacity: 1px;
2972 opacity: 1px;
2898 padding: 8px;
2973 padding: 8px;
2899 white-space: pre-wrap;
2974 white-space: pre-wrap;
2900 -webkit-border-radius: 8px 8px 8px 8px;
2975 -webkit-border-radius: 8px 8px 8px 8px;
2901 -khtml-border-radius: 8px 8px 8px 8px;
2976 -khtml-border-radius: 8px 8px 8px 8px;
2902 -moz-border-radius: 8px 8px 8px 8px;
2977 -moz-border-radius: 8px 8px 8px 8px;
2903 border-radius: 8px 8px 8px 8px;
2978 border-radius: 8px 8px 8px 8px;
2904 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
2979 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
2905 }
2980 }
2906
2981
2907 .mentions-container{
2982 .mentions-container{
2908 width: 90% !important;
2983 width: 90% !important;
2909 }
2984 }
2910 .mentions-container .yui-ac-content{
2985 .mentions-container .yui-ac-content{
2911 width: 100% !important;
2986 width: 100% !important;
2912 }
2987 }
2913
2988
2914 .ac {
2989 .ac {
2915 vertical-align: top;
2990 vertical-align: top;
2916 }
2991 }
2917
2992
2918 .ac .yui-ac {
2993 .ac .yui-ac {
2919 position: inherit;
2994 position: inherit;
2920 font-size: 100%;
2995 font-size: 100%;
2921 }
2996 }
2922
2997
2923 .ac .perm_ac {
2998 .ac .perm_ac {
2924 width: 20em;
2999 width: 20em;
2925 }
3000 }
2926
3001
2927 .ac .yui-ac-input {
3002 .ac .yui-ac-input {
2928 width: 100%;
3003 width: 100%;
2929 }
3004 }
2930
3005
2931 .ac .yui-ac-container {
3006 .ac .yui-ac-container {
2932 position: absolute;
3007 position: absolute;
2933 top: 1.6em;
3008 top: 1.6em;
2934 width: auto;
3009 width: auto;
2935 }
3010 }
2936
3011
2937 .ac .yui-ac-content {
3012 .ac .yui-ac-content {
2938 position: absolute;
3013 position: absolute;
2939 border: 1px solid gray;
3014 border: 1px solid gray;
2940 background: #fff;
3015 background: #fff;
2941 z-index: 9050;
3016 z-index: 9050;
2942
3017
2943 }
3018 }
2944
3019
2945 .ac .yui-ac-shadow {
3020 .ac .yui-ac-shadow {
2946 position: absolute;
3021 position: absolute;
2947 width: 100%;
3022 width: 100%;
2948 background: #000;
3023 background: #000;
2949 -moz-opacity: 0.1px;
3024 -moz-opacity: 0.1px;
2950 opacity: .10;
3025 opacity: .10;
2951 filter: alpha(opacity = 10);
3026 filter: alpha(opacity = 10);
2952 z-index: 9049;
3027 z-index: 9049;
2953 margin: .3em;
3028 margin: .3em;
2954 }
3029 }
2955
3030
2956 .ac .yui-ac-content ul {
3031 .ac .yui-ac-content ul {
2957 width: 100%;
3032 width: 100%;
2958 margin: 0;
3033 margin: 0;
2959 padding: 0;
3034 padding: 0;
2960 z-index: 9050;
3035 z-index: 9050;
2961 }
3036 }
2962
3037
2963 .ac .yui-ac-content li {
3038 .ac .yui-ac-content li {
2964 cursor: default;
3039 cursor: default;
2965 white-space: nowrap;
3040 white-space: nowrap;
2966 margin: 0;
3041 margin: 0;
2967 padding: 2px 5px;
3042 padding: 2px 5px;
2968 height: 18px;
3043 height: 18px;
2969 z-index: 9050;
3044 z-index: 9050;
2970 display: block;
3045 display: block;
2971 width: auto !important;
3046 width: auto !important;
2972 }
3047 }
2973
3048
2974 .ac .yui-ac-content li .ac-container-wrap{
3049 .ac .yui-ac-content li .ac-container-wrap{
2975 width: auto;
3050 width: auto;
2976 }
3051 }
2977
3052
2978 .ac .yui-ac-content li.yui-ac-prehighlight {
3053 .ac .yui-ac-content li.yui-ac-prehighlight {
2979 background: #B3D4FF;
3054 background: #B3D4FF;
2980 z-index: 9050;
3055 z-index: 9050;
2981 }
3056 }
2982
3057
2983 .ac .yui-ac-content li.yui-ac-highlight {
3058 .ac .yui-ac-content li.yui-ac-highlight {
2984 background: #556CB5;
3059 background: #556CB5;
2985 color: #FFF;
3060 color: #FFF;
2986 z-index: 9050;
3061 z-index: 9050;
2987 }
3062 }
2988 .ac .yui-ac-bd{
3063 .ac .yui-ac-bd{
2989 z-index: 9050;
3064 z-index: 9050;
2990 }
3065 }
2991
3066
2992 .follow {
3067 .follow {
2993 background: url("../images/icons/heart_add.png") no-repeat scroll 3px;
3068 background: url("../images/icons/heart_add.png") no-repeat scroll 3px;
2994 height: 16px;
3069 height: 16px;
2995 width: 20px;
3070 width: 20px;
2996 cursor: pointer;
3071 cursor: pointer;
2997 display: block;
3072 display: block;
2998 float: right;
3073 float: right;
2999 margin-top: 2px;
3074 margin-top: 2px;
3000 }
3075 }
3001
3076
3002 .following {
3077 .following {
3003 background: url("../images/icons/heart_delete.png") no-repeat scroll 3px;
3078 background: url("../images/icons/heart_delete.png") no-repeat scroll 3px;
3004 height: 16px;
3079 height: 16px;
3005 width: 20px;
3080 width: 20px;
3006 cursor: pointer;
3081 cursor: pointer;
3007 display: block;
3082 display: block;
3008 float: right;
3083 float: right;
3009 margin-top: 2px;
3084 margin-top: 2px;
3010 }
3085 }
3011
3086
3012 .currently_following {
3087 .currently_following {
3013 padding-left: 10px;
3088 padding-left: 10px;
3014 padding-bottom: 5px;
3089 padding-bottom: 5px;
3015 }
3090 }
3016
3091
3017 .add_icon {
3092 .add_icon {
3018 background: url("../images/icons/add.png") no-repeat scroll 3px;
3093 background: url("../images/icons/add.png") no-repeat scroll 3px;
3019 padding-left: 20px;
3094 padding-left: 20px;
3020 padding-top: 0px;
3095 padding-top: 0px;
3021 text-align: left;
3096 text-align: left;
3022 }
3097 }
3023
3098
3024 .accept_icon {
3099 .accept_icon {
3025 background: url("../images/icons/accept.png") no-repeat scroll 3px;
3100 background: url("../images/icons/accept.png") no-repeat scroll 3px;
3026 padding-left: 20px;
3101 padding-left: 20px;
3027 padding-top: 0px;
3102 padding-top: 0px;
3028 text-align: left;
3103 text-align: left;
3029 }
3104 }
3030
3105
3031 .edit_icon {
3106 .edit_icon {
3032 background: url("../images/icons/folder_edit.png") no-repeat scroll 3px;
3107 background: url("../images/icons/folder_edit.png") no-repeat scroll 3px;
3033 padding-left: 20px;
3108 padding-left: 20px;
3034 padding-top: 0px;
3109 padding-top: 0px;
3035 text-align: left;
3110 text-align: left;
3036 }
3111 }
3037
3112
3038 .delete_icon {
3113 .delete_icon {
3039 background: url("../images/icons/delete.png") no-repeat scroll 3px;
3114 background: url("../images/icons/delete.png") no-repeat scroll 3px;
3040 padding-left: 20px;
3115 padding-left: 20px;
3041 padding-top: 0px;
3116 padding-top: 0px;
3042 text-align: left;
3117 text-align: left;
3043 }
3118 }
3044
3119
3045 .refresh_icon {
3120 .refresh_icon {
3046 background: url("../images/icons/arrow_refresh.png") no-repeat scroll
3121 background: url("../images/icons/arrow_refresh.png") no-repeat scroll
3047 3px;
3122 3px;
3048 padding-left: 20px;
3123 padding-left: 20px;
3049 padding-top: 0px;
3124 padding-top: 0px;
3050 text-align: left;
3125 text-align: left;
3051 }
3126 }
3052
3127
3053 .pull_icon {
3128 .pull_icon {
3054 background: url("../images/icons/connect.png") no-repeat scroll 3px;
3129 background: url("../images/icons/connect.png") no-repeat scroll 3px;
3055 padding-left: 20px;
3130 padding-left: 20px;
3056 padding-top: 0px;
3131 padding-top: 0px;
3057 text-align: left;
3132 text-align: left;
3058 }
3133 }
3059
3134
3060 .rss_icon {
3135 .rss_icon {
3061 background: url("../images/icons/rss_16.png") no-repeat scroll 3px;
3136 background: url("../images/icons/rss_16.png") no-repeat scroll 3px;
3062 padding-left: 20px;
3137 padding-left: 20px;
3063 padding-top: 4px;
3138 padding-top: 4px;
3064 text-align: left;
3139 text-align: left;
3065 font-size: 8px
3140 font-size: 8px
3066 }
3141 }
3067
3142
3068 .atom_icon {
3143 .atom_icon {
3069 background: url("../images/icons/atom.png") no-repeat scroll 3px;
3144 background: url("../images/icons/atom.png") no-repeat scroll 3px;
3070 padding-left: 20px;
3145 padding-left: 20px;
3071 padding-top: 4px;
3146 padding-top: 4px;
3072 text-align: left;
3147 text-align: left;
3073 font-size: 8px
3148 font-size: 8px
3074 }
3149 }
3075
3150
3076 .archive_icon {
3151 .archive_icon {
3077 background: url("../images/icons/compress.png") no-repeat scroll 3px;
3152 background: url("../images/icons/compress.png") no-repeat scroll 3px;
3078 padding-left: 20px;
3153 padding-left: 20px;
3079 text-align: left;
3154 text-align: left;
3080 padding-top: 1px;
3155 padding-top: 1px;
3081 }
3156 }
3082
3157
3083 .start_following_icon {
3158 .start_following_icon {
3084 background: url("../images/icons/heart_add.png") no-repeat scroll 3px;
3159 background: url("../images/icons/heart_add.png") no-repeat scroll 3px;
3085 padding-left: 20px;
3160 padding-left: 20px;
3086 text-align: left;
3161 text-align: left;
3087 padding-top: 0px;
3162 padding-top: 0px;
3088 }
3163 }
3089
3164
3090 .stop_following_icon {
3165 .stop_following_icon {
3091 background: url("../images/icons/heart_delete.png") no-repeat scroll 3px;
3166 background: url("../images/icons/heart_delete.png") no-repeat scroll 3px;
3092 padding-left: 20px;
3167 padding-left: 20px;
3093 text-align: left;
3168 text-align: left;
3094 padding-top: 0px;
3169 padding-top: 0px;
3095 }
3170 }
3096
3171
3097 .action_button {
3172 .action_button {
3098 border: 0;
3173 border: 0;
3099 display: inline;
3174 display: inline;
3100 }
3175 }
3101
3176
3102 .action_button:hover {
3177 .action_button:hover {
3103 border: 0;
3178 border: 0;
3104 text-decoration: underline;
3179 text-decoration: underline;
3105 cursor: pointer;
3180 cursor: pointer;
3106 }
3181 }
3107
3182
3108 #switch_repos {
3183 #switch_repos {
3109 position: absolute;
3184 position: absolute;
3110 height: 25px;
3185 height: 25px;
3111 z-index: 1;
3186 z-index: 1;
3112 }
3187 }
3113
3188
3114 #switch_repos select {
3189 #switch_repos select {
3115 min-width: 150px;
3190 min-width: 150px;
3116 max-height: 250px;
3191 max-height: 250px;
3117 z-index: 1;
3192 z-index: 1;
3118 }
3193 }
3119
3194
3120 .breadcrumbs {
3195 .breadcrumbs {
3121 border: medium none;
3196 border: medium none;
3122 color: #FFF;
3197 color: #FFF;
3123 float: left;
3198 float: left;
3124 text-transform: uppercase;
3199 text-transform: uppercase;
3125 font-weight: 700;
3200 font-weight: 700;
3126 font-size: 14px;
3201 font-size: 14px;
3127 margin: 0;
3202 margin: 0;
3128 padding: 11px 0 11px 10px;
3203 padding: 11px 0 11px 10px;
3129 }
3204 }
3130
3205
3131 .breadcrumbs .hash {
3206 .breadcrumbs .hash {
3132 text-transform: none;
3207 text-transform: none;
3133 color: #fff;
3208 color: #fff;
3134 }
3209 }
3135
3210
3136 .breadcrumbs a {
3211 .breadcrumbs a {
3137 color: #FFF;
3212 color: #FFF;
3138 }
3213 }
3139
3214
3140 .flash_msg {
3215 .flash_msg {
3141
3216
3142 }
3217 }
3143
3218
3144 .flash_msg ul {
3219 .flash_msg ul {
3145
3220
3146 }
3221 }
3147
3222
3148 .error_msg {
3223 .error_msg {
3149 background-color: #c43c35;
3224 background-color: #c43c35;
3150 background-repeat: repeat-x;
3225 background-repeat: repeat-x;
3151 background-image: -khtml-gradient(linear, left top, left bottom, from(#ee5f5b), to(#c43c35) );
3226 background-image: -khtml-gradient(linear, left top, left bottom, from(#ee5f5b), to(#c43c35) );
3152 background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35);
3227 background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35);
3153 background-image: -ms-linear-gradient(top, #ee5f5b, #c43c35);
3228 background-image: -ms-linear-gradient(top, #ee5f5b, #c43c35);
3154 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ee5f5b), color-stop(100%, #c43c35) );
3229 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ee5f5b), color-stop(100%, #c43c35) );
3155 background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35);
3230 background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35);
3156 background-image: -o-linear-gradient(top, #ee5f5b, #c43c35);
3231 background-image: -o-linear-gradient(top, #ee5f5b, #c43c35);
3157 background-image: linear-gradient(top, #ee5f5b, #c43c35);
3232 background-image: linear-gradient(top, #ee5f5b, #c43c35);
3158 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b',endColorstr='#c43c35', GradientType=0 );
3233 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b',endColorstr='#c43c35', GradientType=0 );
3159 border-color: #c43c35 #c43c35 #882a25;
3234 border-color: #c43c35 #c43c35 #882a25;
3160 }
3235 }
3161
3236
3162 .warning_msg {
3237 .warning_msg {
3163 color: #404040 !important;
3238 color: #404040 !important;
3164 background-color: #eedc94;
3239 background-color: #eedc94;
3165 background-repeat: repeat-x;
3240 background-repeat: repeat-x;
3166 background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1), to(#eedc94) );
3241 background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1), to(#eedc94) );
3167 background-image: -moz-linear-gradient(top, #fceec1, #eedc94);
3242 background-image: -moz-linear-gradient(top, #fceec1, #eedc94);
3168 background-image: -ms-linear-gradient(top, #fceec1, #eedc94);
3243 background-image: -ms-linear-gradient(top, #fceec1, #eedc94);
3169 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fceec1), color-stop(100%, #eedc94) );
3244 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fceec1), color-stop(100%, #eedc94) );
3170 background-image: -webkit-linear-gradient(top, #fceec1, #eedc94);
3245 background-image: -webkit-linear-gradient(top, #fceec1, #eedc94);
3171 background-image: -o-linear-gradient(top, #fceec1, #eedc94);
3246 background-image: -o-linear-gradient(top, #fceec1, #eedc94);
3172 background-image: linear-gradient(top, #fceec1, #eedc94);
3247 background-image: linear-gradient(top, #fceec1, #eedc94);
3173 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fceec1', endColorstr='#eedc94', GradientType=0 );
3248 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fceec1', endColorstr='#eedc94', GradientType=0 );
3174 border-color: #eedc94 #eedc94 #e4c652;
3249 border-color: #eedc94 #eedc94 #e4c652;
3175 }
3250 }
3176
3251
3177 .success_msg {
3252 .success_msg {
3178 background-color: #57a957;
3253 background-color: #57a957;
3179 background-repeat: repeat-x !important;
3254 background-repeat: repeat-x !important;
3180 background-image: -khtml-gradient(linear, left top, left bottom, from(#62c462), to(#57a957) );
3255 background-image: -khtml-gradient(linear, left top, left bottom, from(#62c462), to(#57a957) );
3181 background-image: -moz-linear-gradient(top, #62c462, #57a957);
3256 background-image: -moz-linear-gradient(top, #62c462, #57a957);
3182 background-image: -ms-linear-gradient(top, #62c462, #57a957);
3257 background-image: -ms-linear-gradient(top, #62c462, #57a957);
3183 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #62c462), color-stop(100%, #57a957) );
3258 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #62c462), color-stop(100%, #57a957) );
3184 background-image: -webkit-linear-gradient(top, #62c462, #57a957);
3259 background-image: -webkit-linear-gradient(top, #62c462, #57a957);
3185 background-image: -o-linear-gradient(top, #62c462, #57a957);
3260 background-image: -o-linear-gradient(top, #62c462, #57a957);
3186 background-image: linear-gradient(top, #62c462, #57a957);
3261 background-image: linear-gradient(top, #62c462, #57a957);
3187 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0 );
3262 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0 );
3188 border-color: #57a957 #57a957 #3d773d;
3263 border-color: #57a957 #57a957 #3d773d;
3189 }
3264 }
3190
3265
3191 .notice_msg {
3266 .notice_msg {
3192 background-color: #339bb9;
3267 background-color: #339bb9;
3193 background-repeat: repeat-x;
3268 background-repeat: repeat-x;
3194 background-image: -khtml-gradient(linear, left top, left bottom, from(#5bc0de), to(#339bb9) );
3269 background-image: -khtml-gradient(linear, left top, left bottom, from(#5bc0de), to(#339bb9) );
3195 background-image: -moz-linear-gradient(top, #5bc0de, #339bb9);
3270 background-image: -moz-linear-gradient(top, #5bc0de, #339bb9);
3196 background-image: -ms-linear-gradient(top, #5bc0de, #339bb9);
3271 background-image: -ms-linear-gradient(top, #5bc0de, #339bb9);
3197 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #5bc0de), color-stop(100%, #339bb9) );
3272 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #5bc0de), color-stop(100%, #339bb9) );
3198 background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9);
3273 background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9);
3199 background-image: -o-linear-gradient(top, #5bc0de, #339bb9);
3274 background-image: -o-linear-gradient(top, #5bc0de, #339bb9);
3200 background-image: linear-gradient(top, #5bc0de, #339bb9);
3275 background-image: linear-gradient(top, #5bc0de, #339bb9);
3201 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0 );
3276 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0 );
3202 border-color: #339bb9 #339bb9 #22697d;
3277 border-color: #339bb9 #339bb9 #22697d;
3203 }
3278 }
3204
3279
3205 .success_msg,.error_msg,.notice_msg,.warning_msg {
3280 .success_msg,.error_msg,.notice_msg,.warning_msg {
3206 font-size: 12px;
3281 font-size: 12px;
3207 font-weight: 700;
3282 font-weight: 700;
3208 min-height: 14px;
3283 min-height: 14px;
3209 line-height: 14px;
3284 line-height: 14px;
3210 margin-bottom: 10px;
3285 margin-bottom: 10px;
3211 margin-top: 0;
3286 margin-top: 0;
3212 display: block;
3287 display: block;
3213 overflow: auto;
3288 overflow: auto;
3214 padding: 6px 10px 6px 10px;
3289 padding: 6px 10px 6px 10px;
3215 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
3290 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
3216 position: relative;
3291 position: relative;
3217 color: #FFF;
3292 color: #FFF;
3218 border-width: 1px;
3293 border-width: 1px;
3219 border-style: solid;
3294 border-style: solid;
3220 -webkit-border-radius: 4px;
3295 -webkit-border-radius: 4px;
3221 -moz-border-radius: 4px;
3296 -moz-border-radius: 4px;
3222 border-radius: 4px;
3297 border-radius: 4px;
3223 -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
3298 -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
3224 -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
3299 -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
3225 box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
3300 box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
3226 }
3301 }
3227
3302
3228 #msg_close {
3303 #msg_close {
3229 background: transparent url("../icons/cross_grey_small.png") no-repeat scroll 0 0;
3304 background: transparent url("../icons/cross_grey_small.png") no-repeat scroll 0 0;
3230 cursor: pointer;
3305 cursor: pointer;
3231 height: 16px;
3306 height: 16px;
3232 position: absolute;
3307 position: absolute;
3233 right: 5px;
3308 right: 5px;
3234 top: 5px;
3309 top: 5px;
3235 width: 16px;
3310 width: 16px;
3236 }
3311 }
3237 div#legend_data{
3312 div#legend_data{
3238 padding-left:10px;
3313 padding-left:10px;
3239 }
3314 }
3240 div#legend_container table{
3315 div#legend_container table{
3241 border: none !important;
3316 border: none !important;
3242 }
3317 }
3243 div#legend_container table,div#legend_choices table {
3318 div#legend_container table,div#legend_choices table {
3244 width: auto !important;
3319 width: auto !important;
3245 }
3320 }
3246
3321
3247 table#permissions_manage {
3322 table#permissions_manage {
3248 width: 0 !important;
3323 width: 0 !important;
3249 }
3324 }
3250
3325
3251 table#permissions_manage span.private_repo_msg {
3326 table#permissions_manage span.private_repo_msg {
3252 font-size: 0.8em;
3327 font-size: 0.8em;
3253 opacity: 0.6px;
3328 opacity: 0.6px;
3254 }
3329 }
3255
3330
3256 table#permissions_manage td.private_repo_msg {
3331 table#permissions_manage td.private_repo_msg {
3257 font-size: 0.8em;
3332 font-size: 0.8em;
3258 }
3333 }
3259
3334
3260 table#permissions_manage tr#add_perm_input td {
3335 table#permissions_manage tr#add_perm_input td {
3261 vertical-align: middle;
3336 vertical-align: middle;
3262 }
3337 }
3263
3338
3264 div.gravatar {
3339 div.gravatar {
3265 background-color: #FFF;
3340 background-color: #FFF;
3266 float: left;
3341 float: left;
3267 margin-right: 0.7em;
3342 margin-right: 0.7em;
3268 padding: 1px 1px 1px 1px;
3343 padding: 1px 1px 1px 1px;
3269 line-height:0;
3344 line-height:0;
3270 -webkit-border-radius: 3px;
3345 -webkit-border-radius: 3px;
3271 -khtml-border-radius: 3px;
3346 -khtml-border-radius: 3px;
3272 -moz-border-radius: 3px;
3347 -moz-border-radius: 3px;
3273 border-radius: 3px;
3348 border-radius: 3px;
3274 }
3349 }
3275
3350
3276 div.gravatar img {
3351 div.gravatar img {
3277 -webkit-border-radius: 2px;
3352 -webkit-border-radius: 2px;
3278 -khtml-border-radius: 2px;
3353 -khtml-border-radius: 2px;
3279 -moz-border-radius: 2px;
3354 -moz-border-radius: 2px;
3280 border-radius: 2px;
3355 border-radius: 2px;
3281 }
3356 }
3282
3357
3283 #header,#content,#footer {
3358 #header,#content,#footer {
3284 min-width: 978px;
3359 min-width: 978px;
3285 }
3360 }
3286
3361
3287 #content {
3362 #content {
3288 clear: both;
3363 clear: both;
3289 overflow: hidden;
3364 overflow: hidden;
3290 padding: 54px 10px 14px 10px;
3365 padding: 54px 10px 14px 10px;
3291 }
3366 }
3292
3367
3293 #content div.box div.title div.search {
3368 #content div.box div.title div.search {
3294
3369
3295 border-left: 1px solid #316293;
3370 border-left: 1px solid #316293;
3296 }
3371 }
3297
3372
3298 #content div.box div.title div.search div.input input {
3373 #content div.box div.title div.search div.input input {
3299 border: 1px solid #316293;
3374 border: 1px solid #316293;
3300 }
3375 }
3301
3376
3302 .ui-btn{
3377 .ui-btn{
3303 color: #515151;
3378 color: #515151;
3304 background-color: #DADADA;
3379 background-color: #DADADA;
3305 background-repeat: repeat-x;
3380 background-repeat: repeat-x;
3306 background-image: -khtml-gradient(linear, left top, left bottom, from(#F4F4F4),to(#DADADA) );
3381 background-image: -khtml-gradient(linear, left top, left bottom, from(#F4F4F4),to(#DADADA) );
3307 background-image: -moz-linear-gradient(top, #F4F4F4, #DADADA);
3382 background-image: -moz-linear-gradient(top, #F4F4F4, #DADADA);
3308 background-image: -ms-linear-gradient(top, #F4F4F4, #DADADA);
3383 background-image: -ms-linear-gradient(top, #F4F4F4, #DADADA);
3309 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #F4F4F4),color-stop(100%, #DADADA) );
3384 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #F4F4F4),color-stop(100%, #DADADA) );
3310 background-image: -webkit-linear-gradient(top, #F4F4F4, #DADADA) );
3385 background-image: -webkit-linear-gradient(top, #F4F4F4, #DADADA) );
3311 background-image: -o-linear-gradient(top, #F4F4F4, #DADADA) );
3386 background-image: -o-linear-gradient(top, #F4F4F4, #DADADA) );
3312 background-image: linear-gradient(top, #F4F4F4, #DADADA);
3387 background-image: linear-gradient(top, #F4F4F4, #DADADA);
3313 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#F4F4F4', endColorstr='#DADADA', GradientType=0);
3388 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#F4F4F4', endColorstr='#DADADA', GradientType=0);
3314
3389
3315 border-top: 1px solid #DDD;
3390 border-top: 1px solid #DDD;
3316 border-left: 1px solid #c6c6c6;
3391 border-left: 1px solid #c6c6c6;
3317 border-right: 1px solid #DDD;
3392 border-right: 1px solid #DDD;
3318 border-bottom: 1px solid #c6c6c6;
3393 border-bottom: 1px solid #c6c6c6;
3319 color: #515151;
3394 color: #515151;
3320 outline: none;
3395 outline: none;
3321 margin: 0px 3px 3px 0px;
3396 margin: 0px 3px 3px 0px;
3322 -webkit-border-radius: 4px 4px 4px 4px !important;
3397 -webkit-border-radius: 4px 4px 4px 4px !important;
3323 -khtml-border-radius: 4px 4px 4px 4px !important;
3398 -khtml-border-radius: 4px 4px 4px 4px !important;
3324 -moz-border-radius: 4px 4px 4px 4px !important;
3399 -moz-border-radius: 4px 4px 4px 4px !important;
3325 border-radius: 4px 4px 4px 4px !important;
3400 border-radius: 4px 4px 4px 4px !important;
3326 cursor: pointer !important;
3401 cursor: pointer !important;
3327 padding: 3px 3px 3px 3px;
3402 padding: 3px 3px 3px 3px;
3328 background-position: 0 -15px;
3403 background-position: 0 -15px;
3329
3404
3330 }
3405 }
3331 .ui-btn.xsmall{
3406 .ui-btn.xsmall{
3332 padding: 1px 2px 1px 1px;
3407 padding: 1px 2px 1px 1px;
3333 }
3408 }
3334
3409
3335 .ui-btn.large{
3410 .ui-btn.large{
3336 padding: 6px 12px;
3411 padding: 6px 12px;
3337 }
3412 }
3338
3413
3339 .ui-btn.clone{
3414 .ui-btn.clone{
3340 padding: 5px 2px 6px 1px;
3415 padding: 5px 2px 6px 1px;
3341 margin: 0px -4px 3px 0px;
3416 margin: 0px -4px 3px 0px;
3342 -webkit-border-radius: 4px 0px 0px 4px !important;
3417 -webkit-border-radius: 4px 0px 0px 4px !important;
3343 -khtml-border-radius: 4px 0px 0px 4px !important;
3418 -khtml-border-radius: 4px 0px 0px 4px !important;
3344 -moz-border-radius: 4px 0px 0px 4px !important;
3419 -moz-border-radius: 4px 0px 0px 4px !important;
3345 border-radius: 4px 0px 0px 4px !important;
3420 border-radius: 4px 0px 0px 4px !important;
3346 width: 100px;
3421 width: 100px;
3347 text-align: center;
3422 text-align: center;
3348 float: left;
3423 float: left;
3349 position: absolute;
3424 position: absolute;
3350 }
3425 }
3351 .ui-btn:focus {
3426 .ui-btn:focus {
3352 outline: none;
3427 outline: none;
3353 }
3428 }
3354 .ui-btn:hover{
3429 .ui-btn:hover{
3355 background-position: 0 0px;
3430 background-position: 0 0px;
3356 text-decoration: none;
3431 text-decoration: none;
3357 color: #515151;
3432 color: #515151;
3358 box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25), 0 0 3px #FFFFFF !important;
3433 box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25), 0 0 3px #FFFFFF !important;
3359 }
3434 }
3360
3435
3361 .ui-btn.red{
3436 .ui-btn.red{
3362 color:#fff;
3437 color:#fff;
3363 background-color: #c43c35;
3438 background-color: #c43c35;
3364 background-repeat: repeat-x;
3439 background-repeat: repeat-x;
3365 background-image: -khtml-gradient(linear, left top, left bottom, from(#ee5f5b), to(#c43c35));
3440 background-image: -khtml-gradient(linear, left top, left bottom, from(#ee5f5b), to(#c43c35));
3366 background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35);
3441 background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35);
3367 background-image: -ms-linear-gradient(top, #ee5f5b, #c43c35);
3442 background-image: -ms-linear-gradient(top, #ee5f5b, #c43c35);
3368 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ee5f5b), color-stop(100%, #c43c35));
3443 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ee5f5b), color-stop(100%, #c43c35));
3369 background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35);
3444 background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35);
3370 background-image: -o-linear-gradient(top, #ee5f5b, #c43c35);
3445 background-image: -o-linear-gradient(top, #ee5f5b, #c43c35);
3371 background-image: linear-gradient(top, #ee5f5b, #c43c35);
3446 background-image: linear-gradient(top, #ee5f5b, #c43c35);
3372 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0);
3447 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0);
3373 border-color: #c43c35 #c43c35 #882a25;
3448 border-color: #c43c35 #c43c35 #882a25;
3374 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
3449 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
3375 }
3450 }
3376
3451
3377
3452
3378 .ui-btn.blue{
3453 .ui-btn.blue{
3379 color:#fff;
3454 color:#fff;
3380 background-color: #339bb9;
3455 background-color: #339bb9;
3381 background-repeat: repeat-x;
3456 background-repeat: repeat-x;
3382 background-image: -khtml-gradient(linear, left top, left bottom, from(#5bc0de), to(#339bb9));
3457 background-image: -khtml-gradient(linear, left top, left bottom, from(#5bc0de), to(#339bb9));
3383 background-image: -moz-linear-gradient(top, #5bc0de, #339bb9);
3458 background-image: -moz-linear-gradient(top, #5bc0de, #339bb9);
3384 background-image: -ms-linear-gradient(top, #5bc0de, #339bb9);
3459 background-image: -ms-linear-gradient(top, #5bc0de, #339bb9);
3385 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #5bc0de), color-stop(100%, #339bb9));
3460 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #5bc0de), color-stop(100%, #339bb9));
3386 background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9);
3461 background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9);
3387 background-image: -o-linear-gradient(top, #5bc0de, #339bb9);
3462 background-image: -o-linear-gradient(top, #5bc0de, #339bb9);
3388 background-image: linear-gradient(top, #5bc0de, #339bb9);
3463 background-image: linear-gradient(top, #5bc0de, #339bb9);
3389 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0);
3464 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0);
3390 border-color: #339bb9 #339bb9 #22697d;
3465 border-color: #339bb9 #339bb9 #22697d;
3391 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
3466 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
3392 }
3467 }
3393
3468
3394 .ui-btn.green{
3469 .ui-btn.green{
3395 background-color: #57a957;
3470 background-color: #57a957;
3396 background-repeat: repeat-x;
3471 background-repeat: repeat-x;
3397 background-image: -khtml-gradient(linear, left top, left bottom, from(#62c462), to(#57a957));
3472 background-image: -khtml-gradient(linear, left top, left bottom, from(#62c462), to(#57a957));
3398 background-image: -moz-linear-gradient(top, #62c462, #57a957);
3473 background-image: -moz-linear-gradient(top, #62c462, #57a957);
3399 background-image: -ms-linear-gradient(top, #62c462, #57a957);
3474 background-image: -ms-linear-gradient(top, #62c462, #57a957);
3400 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #62c462), color-stop(100%, #57a957));
3475 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #62c462), color-stop(100%, #57a957));
3401 background-image: -webkit-linear-gradient(top, #62c462, #57a957);
3476 background-image: -webkit-linear-gradient(top, #62c462, #57a957);
3402 background-image: -o-linear-gradient(top, #62c462, #57a957);
3477 background-image: -o-linear-gradient(top, #62c462, #57a957);
3403 background-image: linear-gradient(top, #62c462, #57a957);
3478 background-image: linear-gradient(top, #62c462, #57a957);
3404 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0);
3479 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0);
3405 border-color: #57a957 #57a957 #3d773d;
3480 border-color: #57a957 #57a957 #3d773d;
3406 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
3481 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
3407 }
3482 }
3408
3483
3409 .ui-btn.active{
3484 .ui-btn.active{
3410 font-weight: bold;
3485 font-weight: bold;
3411 }
3486 }
3412
3487
3413 ins,div.options a:hover {
3488 ins,div.options a:hover {
3414 text-decoration: none;
3489 text-decoration: none;
3415 }
3490 }
3416
3491
3417 img,
3492 img,
3418 #header #header-inner #quick li a:hover span.normal,
3493 #header #header-inner #quick li a:hover span.normal,
3419 #header #header-inner #quick li ul li.last,
3494 #header #header-inner #quick li ul li.last,
3420 #content div.box div.form div.fields div.field div.textarea table td table td a,
3495 #content div.box div.form div.fields div.field div.textarea table td table td a,
3421 #clone_url,
3496 #clone_url,
3422 #clone_url_id
3497 #clone_url_id
3423 {
3498 {
3424 border: none;
3499 border: none;
3425 }
3500 }
3426
3501
3427 img.icon,.right .merge img {
3502 img.icon,.right .merge img {
3428 vertical-align: bottom;
3503 vertical-align: bottom;
3429 }
3504 }
3430
3505
3431 #header ul#logged-user,#content div.box div.title ul.links,
3506 #header ul#logged-user,#content div.box div.title ul.links,
3432 #content div.box div.message div.dismiss,
3507 #content div.box div.message div.dismiss,
3433 #content div.box div.traffic div.legend ul
3508 #content div.box div.traffic div.legend ul
3434 {
3509 {
3435 float: right;
3510 float: right;
3436 margin: 0;
3511 margin: 0;
3437 padding: 0;
3512 padding: 0;
3438 }
3513 }
3439
3514
3440 #header #header-inner #home,#header #header-inner #logo,
3515 #header #header-inner #home,#header #header-inner #logo,
3441 #content div.box ul.left,#content div.box ol.left,
3516 #content div.box ul.left,#content div.box ol.left,
3442 #content div.box div.pagination-left,div#commit_history,
3517 #content div.box div.pagination-left,div#commit_history,
3443 div#legend_data,div#legend_container,div#legend_choices
3518 div#legend_data,div#legend_container,div#legend_choices
3444 {
3519 {
3445 float: left;
3520 float: left;
3446 }
3521 }
3447
3522
3448 #header #header-inner #quick li:hover ul ul,
3523 #header #header-inner #quick li:hover ul ul,
3449 #header #header-inner #quick li:hover ul ul ul,
3524 #header #header-inner #quick li:hover ul ul ul,
3450 #header #header-inner #quick li:hover ul ul ul ul,
3525 #header #header-inner #quick li:hover ul ul ul ul,
3451 #content #left #menu ul.closed,#content #left #menu li ul.collapsed,.yui-tt-shadow
3526 #content #left #menu ul.closed,#content #left #menu li ul.collapsed,.yui-tt-shadow
3452 {
3527 {
3453 display: none;
3528 display: none;
3454 }
3529 }
3455
3530
3456 #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
3531 #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
3457 {
3532 {
3458 display: block;
3533 display: block;
3459 }
3534 }
3460
3535
3461 #content div.graph {
3536 #content div.graph {
3462 padding: 0 10px 10px;
3537 padding: 0 10px 10px;
3463 }
3538 }
3464
3539
3465 #content div.box div.title ul.links li a:hover,#content div.box div.title ul.links li.ui-tabs-selected a
3540 #content div.box div.title ul.links li a:hover,#content div.box div.title ul.links li.ui-tabs-selected a
3466 {
3541 {
3467 color: #bfe3ff;
3542 color: #bfe3ff;
3468 }
3543 }
3469
3544
3470 #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
3545 #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
3471 {
3546 {
3472 margin: 10px 24px 10px 44px;
3547 margin: 10px 24px 10px 44px;
3473 }
3548 }
3474
3549
3475 #content div.box div.form,#content div.box div.table,#content div.box div.traffic
3550 #content div.box div.form,#content div.box div.table,#content div.box div.traffic
3476 {
3551 {
3477 clear: both;
3552 clear: both;
3478 overflow: hidden;
3553 overflow: hidden;
3479 margin: 0;
3554 margin: 0;
3480 padding: 0 20px 10px;
3555 padding: 0 20px 10px;
3481 }
3556 }
3482
3557
3483 #content div.box div.form div.fields,#login div.form,#login div.form div.fields,#register div.form,#register div.form div.fields
3558 #content div.box div.form div.fields,#login div.form,#login div.form div.fields,#register div.form,#register div.form div.fields
3484 {
3559 {
3485 clear: both;
3560 clear: both;
3486 overflow: hidden;
3561 overflow: hidden;
3487 margin: 0;
3562 margin: 0;
3488 padding: 0;
3563 padding: 0;
3489 }
3564 }
3490
3565
3491 #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
3566 #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
3492 {
3567 {
3493 height: 1%;
3568 height: 1%;
3494 display: block;
3569 display: block;
3495 color: #363636;
3570 color: #363636;
3496 margin: 0;
3571 margin: 0;
3497 padding: 2px 0 0;
3572 padding: 2px 0 0;
3498 }
3573 }
3499
3574
3500 #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
3575 #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
3501 {
3576 {
3502 background: #FBE3E4;
3577 background: #FBE3E4;
3503 border-top: 1px solid #e1b2b3;
3578 border-top: 1px solid #e1b2b3;
3504 border-left: 1px solid #e1b2b3;
3579 border-left: 1px solid #e1b2b3;
3505 border-right: 1px solid #FBC2C4;
3580 border-right: 1px solid #FBC2C4;
3506 border-bottom: 1px solid #FBC2C4;
3581 border-bottom: 1px solid #FBC2C4;
3507 }
3582 }
3508
3583
3509 #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
3584 #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
3510 {
3585 {
3511 background: #E6EFC2;
3586 background: #E6EFC2;
3512 border-top: 1px solid #cebb98;
3587 border-top: 1px solid #cebb98;
3513 border-left: 1px solid #cebb98;
3588 border-left: 1px solid #cebb98;
3514 border-right: 1px solid #c6d880;
3589 border-right: 1px solid #c6d880;
3515 border-bottom: 1px solid #c6d880;
3590 border-bottom: 1px solid #c6d880;
3516 }
3591 }
3517
3592
3518 #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
3593 #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
3519 {
3594 {
3520 margin: 0;
3595 margin: 0;
3521 }
3596 }
3522
3597
3523 #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
3598 #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
3524 {
3599 {
3525 margin: 0 0 0 0px !important;
3600 margin: 0 0 0 0px !important;
3526 padding: 0;
3601 padding: 0;
3527 }
3602 }
3528
3603
3529 #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
3604 #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
3530 {
3605 {
3531 margin: 0 0 0 200px;
3606 margin: 0 0 0 200px;
3532 padding: 0;
3607 padding: 0;
3533 }
3608 }
3534
3609
3535 #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
3610 #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
3536 {
3611 {
3537 color: #000;
3612 color: #000;
3538 text-decoration: none;
3613 text-decoration: none;
3539 }
3614 }
3540
3615
3541 #content div.box div.form div.fields div.field div.select a.ui-selectmenu-focus,#content div.box div.action a.ui-selectmenu-focus
3616 #content div.box div.form div.fields div.field div.select a.ui-selectmenu-focus,#content div.box div.action a.ui-selectmenu-focus
3542 {
3617 {
3543 border: 1px solid #666;
3618 border: 1px solid #666;
3544 }
3619 }
3545
3620
3546 #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
3621 #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
3547 {
3622 {
3548 clear: both;
3623 clear: both;
3549 overflow: hidden;
3624 overflow: hidden;
3550 margin: 0;
3625 margin: 0;
3551 padding: 8px 0 2px;
3626 padding: 8px 0 2px;
3552 }
3627 }
3553
3628
3554 #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
3629 #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
3555 {
3630 {
3556 float: left;
3631 float: left;
3557 margin: 0;
3632 margin: 0;
3558 }
3633 }
3559
3634
3560 #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
3635 #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
3561 {
3636 {
3562 height: 1%;
3637 height: 1%;
3563 display: block;
3638 display: block;
3564 float: left;
3639 float: left;
3565 margin: 2px 0 0 4px;
3640 margin: 2px 0 0 4px;
3566 }
3641 }
3567
3642
3568 div.form div.fields div.field div.button input,
3643 div.form div.fields div.field div.button input,
3569 #content div.box div.form div.fields div.buttons input
3644 #content div.box div.form div.fields div.buttons input
3570 div.form div.fields div.buttons input,
3645 div.form div.fields div.buttons input,
3571 #content div.box div.action div.button input {
3646 #content div.box div.action div.button input {
3572 /*color: #000;*/
3647 /*color: #000;*/
3573 font-size: 11px;
3648 font-size: 11px;
3574 font-weight: 700;
3649 font-weight: 700;
3575 margin: 0;
3650 margin: 0;
3576 }
3651 }
3577
3652
3578 input.ui-button {
3653 input.ui-button {
3579 background: #e5e3e3 url("../images/button.png") repeat-x;
3654 background: #e5e3e3 url("../images/button.png") repeat-x;
3580 border-top: 1px solid #DDD;
3655 border-top: 1px solid #DDD;
3581 border-left: 1px solid #c6c6c6;
3656 border-left: 1px solid #c6c6c6;
3582 border-right: 1px solid #DDD;
3657 border-right: 1px solid #DDD;
3583 border-bottom: 1px solid #c6c6c6;
3658 border-bottom: 1px solid #c6c6c6;
3584 color: #515151 !important;
3659 color: #515151 !important;
3585 outline: none;
3660 outline: none;
3586 margin: 0;
3661 margin: 0;
3587 padding: 6px 12px;
3662 padding: 6px 12px;
3588 -webkit-border-radius: 4px 4px 4px 4px;
3663 -webkit-border-radius: 4px 4px 4px 4px;
3589 -khtml-border-radius: 4px 4px 4px 4px;
3664 -khtml-border-radius: 4px 4px 4px 4px;
3590 -moz-border-radius: 4px 4px 4px 4px;
3665 -moz-border-radius: 4px 4px 4px 4px;
3591 border-radius: 4px 4px 4px 4px;
3666 border-radius: 4px 4px 4px 4px;
3592 box-shadow: 0 1px 0 #ececec;
3667 box-shadow: 0 1px 0 #ececec;
3593 cursor: pointer;
3668 cursor: pointer;
3594 }
3669 }
3595
3670
3596 input.ui-button:hover {
3671 input.ui-button:hover {
3597 background: #b4b4b4 url("../images/button_selected.png") repeat-x;
3672 background: #b4b4b4 url("../images/button_selected.png") repeat-x;
3598 border-top: 1px solid #ccc;
3673 border-top: 1px solid #ccc;
3599 border-left: 1px solid #bebebe;
3674 border-left: 1px solid #bebebe;
3600 border-right: 1px solid #b1b1b1;
3675 border-right: 1px solid #b1b1b1;
3601 border-bottom: 1px solid #afafaf;
3676 border-bottom: 1px solid #afafaf;
3602 }
3677 }
3603
3678
3604 div.form div.fields div.field div.highlight,#content div.box div.form div.fields div.buttons div.highlight
3679 div.form div.fields div.field div.highlight,#content div.box div.form div.fields div.buttons div.highlight
3605 {
3680 {
3606 display: inline;
3681 display: inline;
3607 }
3682 }
3608
3683
3609 #content div.box div.form div.fields div.buttons,div.form div.fields div.buttons
3684 #content div.box div.form div.fields div.buttons,div.form div.fields div.buttons
3610 {
3685 {
3611 margin: 10px 0 0 200px;
3686 margin: 10px 0 0 200px;
3612 padding: 0;
3687 padding: 0;
3613 }
3688 }
3614
3689
3615 #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
3690 #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
3616 {
3691 {
3617 margin: 10px 0 0;
3692 margin: 10px 0 0;
3618 }
3693 }
3619
3694
3620 #content div.box table td.user,#content div.box table td.address {
3695 #content div.box table td.user,#content div.box table td.address {
3621 width: 10%;
3696 width: 10%;
3622 text-align: center;
3697 text-align: center;
3623 }
3698 }
3624
3699
3625 #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
3700 #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
3626 {
3701 {
3627 text-align: right;
3702 text-align: right;
3628 margin: 6px 0 0;
3703 margin: 6px 0 0;
3629 padding: 0;
3704 padding: 0;
3630 }
3705 }
3631
3706
3632 #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
3707 #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
3633 {
3708 {
3634 background: #b4b4b4 url("../images/button_selected.png") repeat-x;
3709 background: #b4b4b4 url("../images/button_selected.png") repeat-x;
3635 border-top: 1px solid #ccc;
3710 border-top: 1px solid #ccc;
3636 border-left: 1px solid #bebebe;
3711 border-left: 1px solid #bebebe;
3637 border-right: 1px solid #b1b1b1;
3712 border-right: 1px solid #b1b1b1;
3638 border-bottom: 1px solid #afafaf;
3713 border-bottom: 1px solid #afafaf;
3639 color: #515151;
3714 color: #515151;
3640 margin: 0;
3715 margin: 0;
3641 padding: 6px 12px;
3716 padding: 6px 12px;
3642 }
3717 }
3643
3718
3644 #content div.box div.pagination div.results,#content div.box div.pagination-wh div.results
3719 #content div.box div.pagination div.results,#content div.box div.pagination-wh div.results
3645 {
3720 {
3646 text-align: left;
3721 text-align: left;
3647 float: left;
3722 float: left;
3648 margin: 0;
3723 margin: 0;
3649 padding: 0;
3724 padding: 0;
3650 }
3725 }
3651
3726
3652 #content div.box div.pagination div.results span,#content div.box div.pagination-wh div.results span
3727 #content div.box div.pagination div.results span,#content div.box div.pagination-wh div.results span
3653 {
3728 {
3654 height: 1%;
3729 height: 1%;
3655 display: block;
3730 display: block;
3656 float: left;
3731 float: left;
3657 background: #ebebeb url("../images/pager.png") repeat-x;
3732 background: #ebebeb url("../images/pager.png") repeat-x;
3658 border-top: 1px solid #dedede;
3733 border-top: 1px solid #dedede;
3659 border-left: 1px solid #cfcfcf;
3734 border-left: 1px solid #cfcfcf;
3660 border-right: 1px solid #c4c4c4;
3735 border-right: 1px solid #c4c4c4;
3661 border-bottom: 1px solid #c4c4c4;
3736 border-bottom: 1px solid #c4c4c4;
3662 color: #4A4A4A;
3737 color: #4A4A4A;
3663 font-weight: 700;
3738 font-weight: 700;
3664 margin: 0;
3739 margin: 0;
3665 padding: 6px 8px;
3740 padding: 6px 8px;
3666 }
3741 }
3667
3742
3668 #content div.box div.pagination ul.pager li.disabled,#content div.box div.pagination-wh a.disabled
3743 #content div.box div.pagination ul.pager li.disabled,#content div.box div.pagination-wh a.disabled
3669 {
3744 {
3670 color: #B4B4B4;
3745 color: #B4B4B4;
3671 padding: 6px;
3746 padding: 6px;
3672 }
3747 }
3673
3748
3674 #login,#register {
3749 #login,#register {
3675 width: 520px;
3750 width: 520px;
3676 margin: 10% auto 0;
3751 margin: 10% auto 0;
3677 padding: 0;
3752 padding: 0;
3678 }
3753 }
3679
3754
3680 #login div.color,#register div.color {
3755 #login div.color,#register div.color {
3681 clear: both;
3756 clear: both;
3682 overflow: hidden;
3757 overflow: hidden;
3683 background: #FFF;
3758 background: #FFF;
3684 margin: 10px auto 0;
3759 margin: 10px auto 0;
3685 padding: 3px 3px 3px 0;
3760 padding: 3px 3px 3px 0;
3686 }
3761 }
3687
3762
3688 #login div.color a,#register div.color a {
3763 #login div.color a,#register div.color a {
3689 width: 20px;
3764 width: 20px;
3690 height: 20px;
3765 height: 20px;
3691 display: block;
3766 display: block;
3692 float: left;
3767 float: left;
3693 margin: 0 0 0 3px;
3768 margin: 0 0 0 3px;
3694 padding: 0;
3769 padding: 0;
3695 }
3770 }
3696
3771
3697 #login div.title h5,#register div.title h5 {
3772 #login div.title h5,#register div.title h5 {
3698 color: #fff;
3773 color: #fff;
3699 margin: 10px;
3774 margin: 10px;
3700 padding: 0;
3775 padding: 0;
3701 }
3776 }
3702
3777
3703 #login div.form div.fields div.field,#register div.form div.fields div.field
3778 #login div.form div.fields div.field,#register div.form div.fields div.field
3704 {
3779 {
3705 clear: both;
3780 clear: both;
3706 overflow: hidden;
3781 overflow: hidden;
3707 margin: 0;
3782 margin: 0;
3708 padding: 0 0 10px;
3783 padding: 0 0 10px;
3709 }
3784 }
3710
3785
3711 #login div.form div.fields div.field span.error-message,#register div.form div.fields div.field span.error-message
3786 #login div.form div.fields div.field span.error-message,#register div.form div.fields div.field span.error-message
3712 {
3787 {
3713 height: 1%;
3788 height: 1%;
3714 display: block;
3789 display: block;
3715 color: red;
3790 color: red;
3716 margin: 8px 0 0;
3791 margin: 8px 0 0;
3717 padding: 0;
3792 padding: 0;
3718 max-width: 320px;
3793 max-width: 320px;
3719 }
3794 }
3720
3795
3721 #login div.form div.fields div.field div.label label,#register div.form div.fields div.field div.label label
3796 #login div.form div.fields div.field div.label label,#register div.form div.fields div.field div.label label
3722 {
3797 {
3723 color: #000;
3798 color: #000;
3724 font-weight: 700;
3799 font-weight: 700;
3725 }
3800 }
3726
3801
3727 #login div.form div.fields div.field div.input,#register div.form div.fields div.field div.input
3802 #login div.form div.fields div.field div.input,#register div.form div.fields div.field div.input
3728 {
3803 {
3729 float: left;
3804 float: left;
3730 margin: 0;
3805 margin: 0;
3731 padding: 0;
3806 padding: 0;
3732 }
3807 }
3733
3808
3734 #login div.form div.fields div.field div.checkbox,#register div.form div.fields div.field div.checkbox
3809 #login div.form div.fields div.field div.checkbox,#register div.form div.fields div.field div.checkbox
3735 {
3810 {
3736 margin: 0 0 0 184px;
3811 margin: 0 0 0 184px;
3737 padding: 0;
3812 padding: 0;
3738 }
3813 }
3739
3814
3740 #login div.form div.fields div.field div.checkbox label,#register div.form div.fields div.field div.checkbox label
3815 #login div.form div.fields div.field div.checkbox label,#register div.form div.fields div.field div.checkbox label
3741 {
3816 {
3742 color: #565656;
3817 color: #565656;
3743 font-weight: 700;
3818 font-weight: 700;
3744 }
3819 }
3745
3820
3746 #login div.form div.fields div.buttons input,#register div.form div.fields div.buttons input
3821 #login div.form div.fields div.buttons input,#register div.form div.fields div.buttons input
3747 {
3822 {
3748 color: #000;
3823 color: #000;
3749 font-size: 1em;
3824 font-size: 1em;
3750 font-weight: 700;
3825 font-weight: 700;
3751 margin: 0;
3826 margin: 0;
3752 }
3827 }
3753
3828
3754 #changeset_content .container .wrapper,#graph_content .container .wrapper
3829 #changeset_content .container .wrapper,#graph_content .container .wrapper
3755 {
3830 {
3756 width: 600px;
3831 width: 600px;
3757 }
3832 }
3758
3833
3759 #changeset_content .container .left {
3834 #changeset_content .container .left {
3760 float: left;
3835 float: left;
3761 width: 75%;
3836 width: 75%;
3762 padding-left: 5px;
3837 padding-left: 5px;
3763 }
3838 }
3764
3839
3765 #changeset_content .container .left .date,.ac .match {
3840 #changeset_content .container .left .date,.ac .match {
3766 font-weight: 700;
3841 font-weight: 700;
3767 padding-top: 5px;
3842 padding-top: 5px;
3768 padding-bottom: 5px;
3843 padding-bottom: 5px;
3769 }
3844 }
3770
3845
3771 div#legend_container table td,div#legend_choices table td {
3846 div#legend_container table td,div#legend_choices table td {
3772 border: none !important;
3847 border: none !important;
3773 height: 20px !important;
3848 height: 20px !important;
3774 padding: 0 !important;
3849 padding: 0 !important;
3775 }
3850 }
3776
3851
3777 .q_filter_box {
3852 .q_filter_box {
3778 -webkit-box-shadow: rgba(0,0,0,0.07) 0 1px 2px inset;
3853 -webkit-box-shadow: rgba(0,0,0,0.07) 0 1px 2px inset;
3779 -webkit-border-radius: 4px;
3854 -webkit-border-radius: 4px;
3780 -moz-border-radius: 4px;
3855 -moz-border-radius: 4px;
3781 border-radius: 4px;
3856 border-radius: 4px;
3782 border: 0 none;
3857 border: 0 none;
3783 color: #AAAAAA;
3858 color: #AAAAAA;
3784 margin-bottom: -4px;
3859 margin-bottom: -4px;
3785 margin-top: -4px;
3860 margin-top: -4px;
3786 padding-left: 3px;
3861 padding-left: 3px;
3787 }
3862 }
3788
3863
3789 #node_filter {
3864 #node_filter {
3790 border: 0px solid #545454;
3865 border: 0px solid #545454;
3791 color: #AAAAAA;
3866 color: #AAAAAA;
3792 padding-left: 3px;
3867 padding-left: 3px;
3793 }
3868 }
3794
3869
3795
3870
3796 .group_members_wrap{
3871 .group_members_wrap{
3797
3872
3798 }
3873 }
3799
3874
3800 .group_members .group_member{
3875 .group_members .group_member{
3801 height: 30px;
3876 height: 30px;
3802 padding:0px 0px 0px 10px;
3877 padding:0px 0px 0px 10px;
3803 }
3878 }
3804
3879
3805 .reviewers_member{
3880 .reviewers_member{
3806 height: 15px;
3881 height: 15px;
3807 padding:0px 0px 0px 10px;
3882 padding:0px 0px 0px 10px;
3808 }
3883 }
3809
3884
3810 .emails_wrap{
3885 .emails_wrap{
3811 padding: 0px 20px;
3886 padding: 0px 20px;
3812 }
3887 }
3813
3888
3814 .emails_wrap .email_entry{
3889 .emails_wrap .email_entry{
3815 height: 30px;
3890 height: 30px;
3816 padding:0px 0px 0px 10px;
3891 padding:0px 0px 0px 10px;
3817 }
3892 }
3818 .emails_wrap .email_entry .email{
3893 .emails_wrap .email_entry .email{
3819 float: left
3894 float: left
3820 }
3895 }
3821 .emails_wrap .email_entry .email_action{
3896 .emails_wrap .email_entry .email_action{
3822 float: left
3897 float: left
3823 }
3898 }
3824
3899
3825 /*README STYLE*/
3900 /*README STYLE*/
3826
3901
3827 div.readme {
3902 div.readme {
3828 padding:0px;
3903 padding:0px;
3829 }
3904 }
3830
3905
3831 div.readme h2 {
3906 div.readme h2 {
3832 font-weight: normal;
3907 font-weight: normal;
3833 }
3908 }
3834
3909
3835 div.readme .readme_box {
3910 div.readme .readme_box {
3836 background-color: #fafafa;
3911 background-color: #fafafa;
3837 }
3912 }
3838
3913
3839 div.readme .readme_box {
3914 div.readme .readme_box {
3840 clear:both;
3915 clear:both;
3841 overflow:hidden;
3916 overflow:hidden;
3842 margin:0;
3917 margin:0;
3843 padding:0 20px 10px;
3918 padding:0 20px 10px;
3844 }
3919 }
3845
3920
3846 div.readme .readme_box h1, div.readme .readme_box h2, div.readme .readme_box h3, div.readme .readme_box h4, div.readme .readme_box h5, div.readme .readme_box h6 {
3921 div.readme .readme_box h1, div.readme .readme_box h2, div.readme .readme_box h3, div.readme .readme_box h4, div.readme .readme_box h5, div.readme .readme_box h6 {
3847 border-bottom: 0 !important;
3922 border-bottom: 0 !important;
3848 margin: 0 !important;
3923 margin: 0 !important;
3849 padding: 0 !important;
3924 padding: 0 !important;
3850 line-height: 1.5em !important;
3925 line-height: 1.5em !important;
3851 }
3926 }
3852
3927
3853
3928
3854 div.readme .readme_box h1:first-child {
3929 div.readme .readme_box h1:first-child {
3855 padding-top: .25em !important;
3930 padding-top: .25em !important;
3856 }
3931 }
3857
3932
3858 div.readme .readme_box h2, div.readme .readme_box h3 {
3933 div.readme .readme_box h2, div.readme .readme_box h3 {
3859 margin: 1em 0 !important;
3934 margin: 1em 0 !important;
3860 }
3935 }
3861
3936
3862 div.readme .readme_box h2 {
3937 div.readme .readme_box h2 {
3863 margin-top: 1.5em !important;
3938 margin-top: 1.5em !important;
3864 border-top: 4px solid #e0e0e0 !important;
3939 border-top: 4px solid #e0e0e0 !important;
3865 padding-top: .5em !important;
3940 padding-top: .5em !important;
3866 }
3941 }
3867
3942
3868 div.readme .readme_box p {
3943 div.readme .readme_box p {
3869 color: black !important;
3944 color: black !important;
3870 margin: 1em 0 !important;
3945 margin: 1em 0 !important;
3871 line-height: 1.5em !important;
3946 line-height: 1.5em !important;
3872 }
3947 }
3873
3948
3874 div.readme .readme_box ul {
3949 div.readme .readme_box ul {
3875 list-style: disc !important;
3950 list-style: disc !important;
3876 margin: 1em 0 1em 2em !important;
3951 margin: 1em 0 1em 2em !important;
3877 }
3952 }
3878
3953
3879 div.readme .readme_box ol {
3954 div.readme .readme_box ol {
3880 list-style: decimal;
3955 list-style: decimal;
3881 margin: 1em 0 1em 2em !important;
3956 margin: 1em 0 1em 2em !important;
3882 }
3957 }
3883
3958
3884 div.readme .readme_box pre, code {
3959 div.readme .readme_box pre, code {
3885 font: 12px "Bitstream Vera Sans Mono","Courier",monospace;
3960 font: 12px "Bitstream Vera Sans Mono","Courier",monospace;
3886 }
3961 }
3887
3962
3888 div.readme .readme_box code {
3963 div.readme .readme_box code {
3889 font-size: 12px !important;
3964 font-size: 12px !important;
3890 background-color: ghostWhite !important;
3965 background-color: ghostWhite !important;
3891 color: #444 !important;
3966 color: #444 !important;
3892 padding: 0 .2em !important;
3967 padding: 0 .2em !important;
3893 border: 1px solid #dedede !important;
3968 border: 1px solid #dedede !important;
3894 }
3969 }
3895
3970
3896 div.readme .readme_box pre code {
3971 div.readme .readme_box pre code {
3897 padding: 0 !important;
3972 padding: 0 !important;
3898 font-size: 12px !important;
3973 font-size: 12px !important;
3899 background-color: #eee !important;
3974 background-color: #eee !important;
3900 border: none !important;
3975 border: none !important;
3901 }
3976 }
3902
3977
3903 div.readme .readme_box pre {
3978 div.readme .readme_box pre {
3904 margin: 1em 0;
3979 margin: 1em 0;
3905 font-size: 12px;
3980 font-size: 12px;
3906 background-color: #eee;
3981 background-color: #eee;
3907 border: 1px solid #ddd;
3982 border: 1px solid #ddd;
3908 padding: 5px;
3983 padding: 5px;
3909 color: #444;
3984 color: #444;
3910 overflow: auto;
3985 overflow: auto;
3911 -webkit-box-shadow: rgba(0,0,0,0.07) 0 1px 2px inset;
3986 -webkit-box-shadow: rgba(0,0,0,0.07) 0 1px 2px inset;
3912 -webkit-border-radius: 3px;
3987 -webkit-border-radius: 3px;
3913 -moz-border-radius: 3px;
3988 -moz-border-radius: 3px;
3914 border-radius: 3px;
3989 border-radius: 3px;
3915 }
3990 }
3916
3991
3917
3992
3918 /** RST STYLE **/
3993 /** RST STYLE **/
3919
3994
3920
3995
3921 div.rst-block {
3996 div.rst-block {
3922 padding:0px;
3997 padding:0px;
3923 }
3998 }
3924
3999
3925 div.rst-block h2 {
4000 div.rst-block h2 {
3926 font-weight: normal;
4001 font-weight: normal;
3927 }
4002 }
3928
4003
3929 div.rst-block {
4004 div.rst-block {
3930 background-color: #fafafa;
4005 background-color: #fafafa;
3931 }
4006 }
3932
4007
3933 div.rst-block {
4008 div.rst-block {
3934 clear:both;
4009 clear:both;
3935 overflow:hidden;
4010 overflow:hidden;
3936 margin:0;
4011 margin:0;
3937 padding:0 20px 10px;
4012 padding:0 20px 10px;
3938 }
4013 }
3939
4014
3940 div.rst-block h1, div.rst-block h2, div.rst-block h3, div.rst-block h4, div.rst-block h5, div.rst-block h6 {
4015 div.rst-block h1, div.rst-block h2, div.rst-block h3, div.rst-block h4, div.rst-block h5, div.rst-block h6 {
3941 border-bottom: 0 !important;
4016 border-bottom: 0 !important;
3942 margin: 0 !important;
4017 margin: 0 !important;
3943 padding: 0 !important;
4018 padding: 0 !important;
3944 line-height: 1.5em !important;
4019 line-height: 1.5em !important;
3945 }
4020 }
3946
4021
3947
4022
3948 div.rst-block h1:first-child {
4023 div.rst-block h1:first-child {
3949 padding-top: .25em !important;
4024 padding-top: .25em !important;
3950 }
4025 }
3951
4026
3952 div.rst-block h2, div.rst-block h3 {
4027 div.rst-block h2, div.rst-block h3 {
3953 margin: 1em 0 !important;
4028 margin: 1em 0 !important;
3954 }
4029 }
3955
4030
3956 div.rst-block h2 {
4031 div.rst-block h2 {
3957 margin-top: 1.5em !important;
4032 margin-top: 1.5em !important;
3958 border-top: 4px solid #e0e0e0 !important;
4033 border-top: 4px solid #e0e0e0 !important;
3959 padding-top: .5em !important;
4034 padding-top: .5em !important;
3960 }
4035 }
3961
4036
3962 div.rst-block p {
4037 div.rst-block p {
3963 color: black !important;
4038 color: black !important;
3964 margin: 1em 0 !important;
4039 margin: 1em 0 !important;
3965 line-height: 1.5em !important;
4040 line-height: 1.5em !important;
3966 }
4041 }
3967
4042
3968 div.rst-block ul {
4043 div.rst-block ul {
3969 list-style: disc !important;
4044 list-style: disc !important;
3970 margin: 1em 0 1em 2em !important;
4045 margin: 1em 0 1em 2em !important;
3971 }
4046 }
3972
4047
3973 div.rst-block ol {
4048 div.rst-block ol {
3974 list-style: decimal;
4049 list-style: decimal;
3975 margin: 1em 0 1em 2em !important;
4050 margin: 1em 0 1em 2em !important;
3976 }
4051 }
3977
4052
3978 div.rst-block pre, code {
4053 div.rst-block pre, code {
3979 font: 12px "Bitstream Vera Sans Mono","Courier",monospace;
4054 font: 12px "Bitstream Vera Sans Mono","Courier",monospace;
3980 }
4055 }
3981
4056
3982 div.rst-block code {
4057 div.rst-block code {
3983 font-size: 12px !important;
4058 font-size: 12px !important;
3984 background-color: ghostWhite !important;
4059 background-color: ghostWhite !important;
3985 color: #444 !important;
4060 color: #444 !important;
3986 padding: 0 .2em !important;
4061 padding: 0 .2em !important;
3987 border: 1px solid #dedede !important;
4062 border: 1px solid #dedede !important;
3988 }
4063 }
3989
4064
3990 div.rst-block pre code {
4065 div.rst-block pre code {
3991 padding: 0 !important;
4066 padding: 0 !important;
3992 font-size: 12px !important;
4067 font-size: 12px !important;
3993 background-color: #eee !important;
4068 background-color: #eee !important;
3994 border: none !important;
4069 border: none !important;
3995 }
4070 }
3996
4071
3997 div.rst-block pre {
4072 div.rst-block pre {
3998 margin: 1em 0;
4073 margin: 1em 0;
3999 font-size: 12px;
4074 font-size: 12px;
4000 background-color: #eee;
4075 background-color: #eee;
4001 border: 1px solid #ddd;
4076 border: 1px solid #ddd;
4002 padding: 5px;
4077 padding: 5px;
4003 color: #444;
4078 color: #444;
4004 overflow: auto;
4079 overflow: auto;
4005 -webkit-box-shadow: rgba(0,0,0,0.07) 0 1px 2px inset;
4080 -webkit-box-shadow: rgba(0,0,0,0.07) 0 1px 2px inset;
4006 -webkit-border-radius: 3px;
4081 -webkit-border-radius: 3px;
4007 -moz-border-radius: 3px;
4082 -moz-border-radius: 3px;
4008 border-radius: 3px;
4083 border-radius: 3px;
4009 }
4084 }
4010
4085
4011
4086
4012 /** comment main **/
4087 /** comment main **/
4013 .comments {
4088 .comments {
4014 padding:10px 20px;
4089 padding:10px 20px;
4015 }
4090 }
4016
4091
4017 .comments .comment {
4092 .comments .comment {
4018 border: 1px solid #ddd;
4093 border: 1px solid #ddd;
4019 margin-top: 10px;
4094 margin-top: 10px;
4020 -webkit-border-radius: 4px;
4095 -webkit-border-radius: 4px;
4021 -moz-border-radius: 4px;
4096 -moz-border-radius: 4px;
4022 border-radius: 4px;
4097 border-radius: 4px;
4023 }
4098 }
4024
4099
4025 .comments .comment .meta {
4100 .comments .comment .meta {
4026 background: #f8f8f8;
4101 background: #f8f8f8;
4027 padding: 4px;
4102 padding: 4px;
4028 border-bottom: 1px solid #ddd;
4103 border-bottom: 1px solid #ddd;
4029 height: 18px;
4104 height: 18px;
4030 }
4105 }
4031
4106
4032 .comments .comment .meta img {
4107 .comments .comment .meta img {
4033 vertical-align: middle;
4108 vertical-align: middle;
4034 }
4109 }
4035
4110
4036 .comments .comment .meta .user {
4111 .comments .comment .meta .user {
4037 font-weight: bold;
4112 font-weight: bold;
4038 float: left;
4113 float: left;
4039 padding: 4px 2px 2px 2px;
4114 padding: 4px 2px 2px 2px;
4040 }
4115 }
4041
4116
4042 .comments .comment .meta .date {
4117 .comments .comment .meta .date {
4043 float: left;
4118 float: left;
4044 padding:4px 4px 0px 4px;
4119 padding:4px 4px 0px 4px;
4045 }
4120 }
4046
4121
4047 .comments .comment .text {
4122 .comments .comment .text {
4048 background-color: #FAFAFA;
4123 background-color: #FAFAFA;
4049 }
4124 }
4050 .comment .text div.rst-block p {
4125 .comment .text div.rst-block p {
4051 margin: 0.5em 0px !important;
4126 margin: 0.5em 0px !important;
4052 }
4127 }
4053
4128
4054 .comments .comments-number{
4129 .comments .comments-number{
4055 padding:0px 0px 10px 0px;
4130 padding:0px 0px 10px 0px;
4056 font-weight: bold;
4131 font-weight: bold;
4057 color: #666;
4132 color: #666;
4058 font-size: 16px;
4133 font-size: 16px;
4059 }
4134 }
4060
4135
4061 /** comment form **/
4136 /** comment form **/
4062
4137
4063 .status-block{
4138 .status-block{
4064 height:80px;
4139 height:80px;
4065 clear:both
4140 clear:both
4066 }
4141 }
4067
4142
4068 .comment-form .clearfix{
4143 .comment-form .clearfix{
4069 background: #EEE;
4144 background: #EEE;
4070 -webkit-border-radius: 4px;
4145 -webkit-border-radius: 4px;
4071 -moz-border-radius: 4px;
4146 -moz-border-radius: 4px;
4072 border-radius: 4px;
4147 border-radius: 4px;
4073 padding: 10px;
4148 padding: 10px;
4074 }
4149 }
4075
4150
4076 div.comment-form {
4151 div.comment-form {
4077 margin-top: 20px;
4152 margin-top: 20px;
4078 }
4153 }
4079
4154
4080 .comment-form strong {
4155 .comment-form strong {
4081 display: block;
4156 display: block;
4082 margin-bottom: 15px;
4157 margin-bottom: 15px;
4083 }
4158 }
4084
4159
4085 .comment-form textarea {
4160 .comment-form textarea {
4086 width: 100%;
4161 width: 100%;
4087 height: 100px;
4162 height: 100px;
4088 font-family: 'Monaco', 'Courier', 'Courier New', monospace;
4163 font-family: 'Monaco', 'Courier', 'Courier New', monospace;
4089 }
4164 }
4090
4165
4091 form.comment-form {
4166 form.comment-form {
4092 margin-top: 10px;
4167 margin-top: 10px;
4093 margin-left: 10px;
4168 margin-left: 10px;
4094 }
4169 }
4095
4170
4096 .comment-form-submit {
4171 .comment-form-submit {
4097 margin-top: 5px;
4172 margin-top: 5px;
4098 margin-left: 525px;
4173 margin-left: 525px;
4099 }
4174 }
4100
4175
4101 .file-comments {
4176 .file-comments {
4102 display: none;
4177 display: none;
4103 }
4178 }
4104
4179
4105 .comment-form .comment {
4180 .comment-form .comment {
4106 margin-left: 10px;
4181 margin-left: 10px;
4107 }
4182 }
4108
4183
4109 .comment-form .comment-help{
4184 .comment-form .comment-help{
4110 padding: 0px 0px 5px 0px;
4185 padding: 0px 0px 5px 0px;
4111 color: #666;
4186 color: #666;
4112 }
4187 }
4113
4188
4114 .comment-form .comment-button{
4189 .comment-form .comment-button{
4115 padding-top:5px;
4190 padding-top:5px;
4116 }
4191 }
4117
4192
4118 .add-another-button {
4193 .add-another-button {
4119 margin-left: 10px;
4194 margin-left: 10px;
4120 margin-top: 10px;
4195 margin-top: 10px;
4121 margin-bottom: 10px;
4196 margin-bottom: 10px;
4122 }
4197 }
4123
4198
4124 .comment .buttons {
4199 .comment .buttons {
4125 float: right;
4200 float: right;
4126 padding:2px 2px 0px 0px;
4201 padding:2px 2px 0px 0px;
4127 }
4202 }
4128
4203
4129
4204
4130 .show-inline-comments{
4205 .show-inline-comments{
4131 position: relative;
4206 position: relative;
4132 top:1px
4207 top:1px
4133 }
4208 }
4134
4209
4135 /** comment inline form **/
4210 /** comment inline form **/
4136 .comment-inline-form .overlay{
4211 .comment-inline-form .overlay{
4137 display: none;
4212 display: none;
4138 }
4213 }
4139 .comment-inline-form .overlay.submitting{
4214 .comment-inline-form .overlay.submitting{
4140 display:block;
4215 display:block;
4141 background: none repeat scroll 0 0 white;
4216 background: none repeat scroll 0 0 white;
4142 font-size: 16px;
4217 font-size: 16px;
4143 opacity: 0.5;
4218 opacity: 0.5;
4144 position: absolute;
4219 position: absolute;
4145 text-align: center;
4220 text-align: center;
4146 vertical-align: top;
4221 vertical-align: top;
4147
4222
4148 }
4223 }
4149 .comment-inline-form .overlay.submitting .overlay-text{
4224 .comment-inline-form .overlay.submitting .overlay-text{
4150 width:100%;
4225 width:100%;
4151 margin-top:5%;
4226 margin-top:5%;
4152 }
4227 }
4153
4228
4154 .comment-inline-form .clearfix{
4229 .comment-inline-form .clearfix{
4155 background: #EEE;
4230 background: #EEE;
4156 -webkit-border-radius: 4px;
4231 -webkit-border-radius: 4px;
4157 -moz-border-radius: 4px;
4232 -moz-border-radius: 4px;
4158 border-radius: 4px;
4233 border-radius: 4px;
4159 padding: 5px;
4234 padding: 5px;
4160 }
4235 }
4161
4236
4162 div.comment-inline-form {
4237 div.comment-inline-form {
4163 margin-top: 5px;
4238 margin-top: 5px;
4164 padding:2px 6px 8px 6px;
4239 padding:2px 6px 8px 6px;
4165
4240
4166 }
4241 }
4167
4242
4168 .comment-inline-form strong {
4243 .comment-inline-form strong {
4169 display: block;
4244 display: block;
4170 margin-bottom: 15px;
4245 margin-bottom: 15px;
4171 }
4246 }
4172
4247
4173 .comment-inline-form textarea {
4248 .comment-inline-form textarea {
4174 width: 100%;
4249 width: 100%;
4175 height: 100px;
4250 height: 100px;
4176 font-family: 'Monaco', 'Courier', 'Courier New', monospace;
4251 font-family: 'Monaco', 'Courier', 'Courier New', monospace;
4177 }
4252 }
4178
4253
4179 form.comment-inline-form {
4254 form.comment-inline-form {
4180 margin-top: 10px;
4255 margin-top: 10px;
4181 margin-left: 10px;
4256 margin-left: 10px;
4182 }
4257 }
4183
4258
4184 .comment-inline-form-submit {
4259 .comment-inline-form-submit {
4185 margin-top: 5px;
4260 margin-top: 5px;
4186 margin-left: 525px;
4261 margin-left: 525px;
4187 }
4262 }
4188
4263
4189 .file-comments {
4264 .file-comments {
4190 display: none;
4265 display: none;
4191 }
4266 }
4192
4267
4193 .comment-inline-form .comment {
4268 .comment-inline-form .comment {
4194 margin-left: 10px;
4269 margin-left: 10px;
4195 }
4270 }
4196
4271
4197 .comment-inline-form .comment-help{
4272 .comment-inline-form .comment-help{
4198 padding: 0px 0px 2px 0px;
4273 padding: 0px 0px 2px 0px;
4199 color: #666666;
4274 color: #666666;
4200 font-size: 10px;
4275 font-size: 10px;
4201 }
4276 }
4202
4277
4203 .comment-inline-form .comment-button{
4278 .comment-inline-form .comment-button{
4204 padding-top:5px;
4279 padding-top:5px;
4205 }
4280 }
4206
4281
4207 /** comment inline **/
4282 /** comment inline **/
4208 .inline-comments {
4283 .inline-comments {
4209 padding:10px 20px;
4284 padding:10px 20px;
4210 }
4285 }
4211
4286
4212 .inline-comments div.rst-block {
4287 .inline-comments div.rst-block {
4213 clear:both;
4288 clear:both;
4214 overflow:hidden;
4289 overflow:hidden;
4215 margin:0;
4290 margin:0;
4216 padding:0 20px 0px;
4291 padding:0 20px 0px;
4217 }
4292 }
4218 .inline-comments .comment {
4293 .inline-comments .comment {
4219 border: 1px solid #ddd;
4294 border: 1px solid #ddd;
4220 -webkit-border-radius: 4px;
4295 -webkit-border-radius: 4px;
4221 -moz-border-radius: 4px;
4296 -moz-border-radius: 4px;
4222 border-radius: 4px;
4297 border-radius: 4px;
4223 margin: 3px 3px 5px 5px;
4298 margin: 3px 3px 5px 5px;
4224 background-color: #FAFAFA;
4299 background-color: #FAFAFA;
4225 }
4300 }
4226 .inline-comments .add-comment {
4301 .inline-comments .add-comment {
4227 padding: 2px 4px 8px 5px;
4302 padding: 2px 4px 8px 5px;
4228 }
4303 }
4229
4304
4230 .inline-comments .comment-wrapp{
4305 .inline-comments .comment-wrapp{
4231 padding:1px;
4306 padding:1px;
4232 }
4307 }
4233 .inline-comments .comment .meta {
4308 .inline-comments .comment .meta {
4234 background: #f8f8f8;
4309 background: #f8f8f8;
4235 padding: 4px;
4310 padding: 4px;
4236 border-bottom: 1px solid #ddd;
4311 border-bottom: 1px solid #ddd;
4237 height: 20px;
4312 height: 20px;
4238 }
4313 }
4239
4314
4240 .inline-comments .comment .meta img {
4315 .inline-comments .comment .meta img {
4241 vertical-align: middle;
4316 vertical-align: middle;
4242 }
4317 }
4243
4318
4244 .inline-comments .comment .meta .user {
4319 .inline-comments .comment .meta .user {
4245 font-weight: bold;
4320 font-weight: bold;
4246 float:left;
4321 float:left;
4247 padding: 3px;
4322 padding: 3px;
4248 }
4323 }
4249
4324
4250 .inline-comments .comment .meta .date {
4325 .inline-comments .comment .meta .date {
4251 float:left;
4326 float:left;
4252 padding: 3px;
4327 padding: 3px;
4253 }
4328 }
4254
4329
4255 .inline-comments .comment .text {
4330 .inline-comments .comment .text {
4256 background-color: #FAFAFA;
4331 background-color: #FAFAFA;
4257 }
4332 }
4258
4333
4259 .inline-comments .comments-number{
4334 .inline-comments .comments-number{
4260 padding:0px 0px 10px 0px;
4335 padding:0px 0px 10px 0px;
4261 font-weight: bold;
4336 font-weight: bold;
4262 color: #666;
4337 color: #666;
4263 font-size: 16px;
4338 font-size: 16px;
4264 }
4339 }
4265 .inline-comments-button .add-comment{
4340 .inline-comments-button .add-comment{
4266 margin:2px 0px 8px 5px !important
4341 margin:2px 0px 8px 5px !important
4267 }
4342 }
4268
4343
4269
4344
4270 .notification-paginator{
4345 .notification-paginator{
4271 padding: 0px 0px 4px 16px;
4346 padding: 0px 0px 4px 16px;
4272 float: left;
4347 float: left;
4273 }
4348 }
4274
4349
4275 .notifications{
4350 .notifications{
4276 border-radius: 4px 4px 4px 4px;
4351 border-radius: 4px 4px 4px 4px;
4277 -webkit-border-radius: 4px;
4352 -webkit-border-radius: 4px;
4278 -moz-border-radius: 4px;
4353 -moz-border-radius: 4px;
4279 float: right;
4354 float: right;
4280 margin: 20px 0px 0px 0px;
4355 margin: 20px 0px 0px 0px;
4281 position: absolute;
4356 position: absolute;
4282 text-align: center;
4357 text-align: center;
4283 width: 26px;
4358 width: 26px;
4284 z-index: 1000;
4359 z-index: 1000;
4285 }
4360 }
4286 .notifications a{
4361 .notifications a{
4287 color:#888 !important;
4362 color:#888 !important;
4288 display: block;
4363 display: block;
4289 font-size: 10px;
4364 font-size: 10px;
4290 background-color: #DEDEDE !important;
4365 background-color: #DEDEDE !important;
4291 border-radius: 2px !important;
4366 border-radius: 2px !important;
4292 -webkit-border-radius: 2px !important;
4367 -webkit-border-radius: 2px !important;
4293 -moz-border-radius: 2px !important;
4368 -moz-border-radius: 2px !important;
4294 }
4369 }
4295 .notifications a:hover{
4370 .notifications a:hover{
4296 text-decoration: none !important;
4371 text-decoration: none !important;
4297 background-color: #EEEFFF !important;
4372 background-color: #EEEFFF !important;
4298 }
4373 }
4299 .notification-header{
4374 .notification-header{
4300 padding-top:6px;
4375 padding-top:6px;
4301 }
4376 }
4302 .notification-header .desc{
4377 .notification-header .desc{
4303 font-size: 16px;
4378 font-size: 16px;
4304 height: 24px;
4379 height: 24px;
4305 float: left
4380 float: left
4306 }
4381 }
4307 .notification-list .container.unread{
4382 .notification-list .container.unread{
4308 background: none repeat scroll 0 0 rgba(255, 255, 180, 0.6);
4383 background: none repeat scroll 0 0 rgba(255, 255, 180, 0.6);
4309 }
4384 }
4310 .notification-header .gravatar{
4385 .notification-header .gravatar{
4311 background: none repeat scroll 0 0 transparent;
4386 background: none repeat scroll 0 0 transparent;
4312 padding: 0px 0px 0px 8px;
4387 padding: 0px 0px 0px 8px;
4313 }
4388 }
4314 .notification-list .container .notification-header .desc{
4389 .notification-list .container .notification-header .desc{
4315 font-weight: bold;
4390 font-weight: bold;
4316 font-size: 17px;
4391 font-size: 17px;
4317 }
4392 }
4318 .notification-table{
4393 .notification-table{
4319 border: 1px solid #ccc;
4394 border: 1px solid #ccc;
4320 -webkit-border-radius: 6px 6px 6px 6px;
4395 -webkit-border-radius: 6px 6px 6px 6px;
4321 -moz-border-radius: 6px 6px 6px 6px;
4396 -moz-border-radius: 6px 6px 6px 6px;
4322 border-radius: 6px 6px 6px 6px;
4397 border-radius: 6px 6px 6px 6px;
4323 clear: both;
4398 clear: both;
4324 margin: 0px 20px 0px 20px;
4399 margin: 0px 20px 0px 20px;
4325 }
4400 }
4326 .notification-header .delete-notifications{
4401 .notification-header .delete-notifications{
4327 float: right;
4402 float: right;
4328 padding-top: 8px;
4403 padding-top: 8px;
4329 cursor: pointer;
4404 cursor: pointer;
4330 }
4405 }
4331 .notification-header .read-notifications{
4406 .notification-header .read-notifications{
4332 float: right;
4407 float: right;
4333 padding-top: 8px;
4408 padding-top: 8px;
4334 cursor: pointer;
4409 cursor: pointer;
4335 }
4410 }
4336 .notification-subject{
4411 .notification-subject{
4337 clear:both;
4412 clear:both;
4338 border-bottom: 1px solid #eee;
4413 border-bottom: 1px solid #eee;
4339 padding:5px 0px 5px 38px;
4414 padding:5px 0px 5px 38px;
4340 }
4415 }
4341
4416
4342 .notification-body{
4417 .notification-body{
4343 clear:both;
4418 clear:both;
4344 margin: 34px 2px 2px 8px
4419 margin: 34px 2px 2px 8px
4345 }
4420 }
4346
4421
4347 /****
4422 /****
4348 PULL REQUESTS
4423 PULL REQUESTS
4349 *****/
4424 *****/
4350 .pullrequests_section_head {
4425 .pullrequests_section_head {
4351 padding:10px 10px 10px 0px;
4426 padding:10px 10px 10px 0px;
4352 font-size:16px;
4427 font-size:16px;
4353 font-weight: bold;
4428 font-weight: bold;
4354 }
4429 }
4355
4430
4356 /****
4431 /****
4357 PERMS
4432 PERMS
4358 *****/
4433 *****/
4359 #perms .perms_section_head {
4434 #perms .perms_section_head {
4360 padding:10px 10px 10px 0px;
4435 padding:10px 10px 10px 0px;
4361 font-size:16px;
4436 font-size:16px;
4362 font-weight: bold;
4437 font-weight: bold;
4363 }
4438 }
4364
4439
4365 #perms .perm_tag{
4440 #perms .perm_tag{
4366 padding: 1px 3px 1px 3px;
4441 padding: 1px 3px 1px 3px;
4367 font-size: 10px;
4442 font-size: 10px;
4368 font-weight: bold;
4443 font-weight: bold;
4369 text-transform: uppercase;
4444 text-transform: uppercase;
4370 white-space: nowrap;
4445 white-space: nowrap;
4371 -webkit-border-radius: 3px;
4446 -webkit-border-radius: 3px;
4372 -moz-border-radius: 3px;
4447 -moz-border-radius: 3px;
4373 border-radius: 3px;
4448 border-radius: 3px;
4374 }
4449 }
4375
4450
4376 #perms .perm_tag.admin{
4451 #perms .perm_tag.admin{
4377 background-color: #B94A48;
4452 background-color: #B94A48;
4378 color: #ffffff;
4453 color: #ffffff;
4379 }
4454 }
4380
4455
4381 #perms .perm_tag.write{
4456 #perms .perm_tag.write{
4382 background-color: #B94A48;
4457 background-color: #B94A48;
4383 color: #ffffff;
4458 color: #ffffff;
4384 }
4459 }
4385
4460
4386 #perms .perm_tag.read{
4461 #perms .perm_tag.read{
4387 background-color: #468847;
4462 background-color: #468847;
4388 color: #ffffff;
4463 color: #ffffff;
4389 }
4464 }
4390
4465
4391 #perms .perm_tag.none{
4466 #perms .perm_tag.none{
4392 background-color: #bfbfbf;
4467 background-color: #bfbfbf;
4393 color: #ffffff;
4468 color: #ffffff;
4394 }
4469 }
4395
4470
4396 .perm-gravatar{
4471 .perm-gravatar{
4397 vertical-align:middle;
4472 vertical-align:middle;
4398 padding:2px;
4473 padding:2px;
4399 }
4474 }
4400 .perm-gravatar-ac{
4475 .perm-gravatar-ac{
4401 vertical-align:middle;
4476 vertical-align:middle;
4402 padding:2px;
4477 padding:2px;
4403 width: 14px;
4478 width: 14px;
4404 height: 14px;
4479 height: 14px;
4405 }
4480 }
4406
4481
4407 /*****************************************************************************
4482 /*****************************************************************************
4408 DIFFS CSS
4483 DIFFS CSS
4409 ******************************************************************************/
4484 ******************************************************************************/
4410
4485
4411 div.diffblock {
4486 div.diffblock {
4412 overflow: auto;
4487 overflow: auto;
4413 padding: 0px;
4488 padding: 0px;
4414 border: 1px solid #ccc;
4489 border: 1px solid #ccc;
4415 background: #f8f8f8;
4490 background: #f8f8f8;
4416 font-size: 100%;
4491 font-size: 100%;
4417 line-height: 100%;
4492 line-height: 100%;
4418 /* new */
4493 /* new */
4419 line-height: 125%;
4494 line-height: 125%;
4420 -webkit-border-radius: 6px 6px 0px 0px;
4495 -webkit-border-radius: 6px 6px 0px 0px;
4421 -moz-border-radius: 6px 6px 0px 0px;
4496 -moz-border-radius: 6px 6px 0px 0px;
4422 border-radius: 6px 6px 0px 0px;
4497 border-radius: 6px 6px 0px 0px;
4423 }
4498 }
4424 div.diffblock.margined{
4499 div.diffblock.margined{
4425 margin: 0px 20px 0px 20px;
4500 margin: 0px 20px 0px 20px;
4426 }
4501 }
4427 div.diffblock .code-header{
4502 div.diffblock .code-header{
4428 border-bottom: 1px solid #CCCCCC;
4503 border-bottom: 1px solid #CCCCCC;
4429 background: #EEEEEE;
4504 background: #EEEEEE;
4430 padding:10px 0 10px 0;
4505 padding:10px 0 10px 0;
4431 height: 14px;
4506 height: 14px;
4432 }
4507 }
4433 div.diffblock .code-header.cv{
4508 div.diffblock .code-header.cv{
4434 height: 34px;
4509 height: 34px;
4435 }
4510 }
4436 div.diffblock .code-header-title{
4511 div.diffblock .code-header-title{
4437 padding: 0px 0px 10px 5px !important;
4512 padding: 0px 0px 10px 5px !important;
4438 margin: 0 !important;
4513 margin: 0 !important;
4439 }
4514 }
4440 div.diffblock .code-header .hash{
4515 div.diffblock .code-header .hash{
4441 float: left;
4516 float: left;
4442 padding: 2px 0 0 2px;
4517 padding: 2px 0 0 2px;
4443 }
4518 }
4444 div.diffblock .code-header .date{
4519 div.diffblock .code-header .date{
4445 float:left;
4520 float:left;
4446 text-transform: uppercase;
4521 text-transform: uppercase;
4447 padding: 2px 0px 0px 2px;
4522 padding: 2px 0px 0px 2px;
4448 }
4523 }
4449 div.diffblock .code-header div{
4524 div.diffblock .code-header div{
4450 margin-left:4px;
4525 margin-left:4px;
4451 font-weight: bold;
4526 font-weight: bold;
4452 font-size: 14px;
4527 font-size: 14px;
4453 }
4528 }
4454 div.diffblock .code-body{
4529 div.diffblock .code-body{
4455 background: #FFFFFF;
4530 background: #FFFFFF;
4456 }
4531 }
4457 div.diffblock pre.raw{
4532 div.diffblock pre.raw{
4458 background: #FFFFFF;
4533 background: #FFFFFF;
4459 color:#000000;
4534 color:#000000;
4460 }
4535 }
4461 table.code-difftable{
4536 table.code-difftable{
4462 border-collapse: collapse;
4537 border-collapse: collapse;
4463 width: 99%;
4538 width: 99%;
4464 }
4539 }
4465 table.code-difftable td {
4540 table.code-difftable td {
4466 padding: 0 !important;
4541 padding: 0 !important;
4467 background: none !important;
4542 background: none !important;
4468 border:0 !important;
4543 border:0 !important;
4469 vertical-align: none !important;
4544 vertical-align: none !important;
4470 }
4545 }
4471 table.code-difftable .context{
4546 table.code-difftable .context{
4472 background:none repeat scroll 0 0 #DDE7EF;
4547 background:none repeat scroll 0 0 #DDE7EF;
4473 }
4548 }
4474 table.code-difftable .add{
4549 table.code-difftable .add{
4475 background:none repeat scroll 0 0 #DDFFDD;
4550 background:none repeat scroll 0 0 #DDFFDD;
4476 }
4551 }
4477 table.code-difftable .add ins{
4552 table.code-difftable .add ins{
4478 background:none repeat scroll 0 0 #AAFFAA;
4553 background:none repeat scroll 0 0 #AAFFAA;
4479 text-decoration:none;
4554 text-decoration:none;
4480 }
4555 }
4481 table.code-difftable .del{
4556 table.code-difftable .del{
4482 background:none repeat scroll 0 0 #FFDDDD;
4557 background:none repeat scroll 0 0 #FFDDDD;
4483 }
4558 }
4484 table.code-difftable .del del{
4559 table.code-difftable .del del{
4485 background:none repeat scroll 0 0 #FFAAAA;
4560 background:none repeat scroll 0 0 #FFAAAA;
4486 text-decoration:none;
4561 text-decoration:none;
4487 }
4562 }
4488
4563
4489 /** LINE NUMBERS **/
4564 /** LINE NUMBERS **/
4490 table.code-difftable .lineno{
4565 table.code-difftable .lineno{
4491
4566
4492 padding-left:2px;
4567 padding-left:2px;
4493 padding-right:2px;
4568 padding-right:2px;
4494 text-align:right;
4569 text-align:right;
4495 width:32px;
4570 width:32px;
4496 -moz-user-select:none;
4571 -moz-user-select:none;
4497 -webkit-user-select: none;
4572 -webkit-user-select: none;
4498 border-right: 1px solid #CCC !important;
4573 border-right: 1px solid #CCC !important;
4499 border-left: 0px solid #CCC !important;
4574 border-left: 0px solid #CCC !important;
4500 border-top: 0px solid #CCC !important;
4575 border-top: 0px solid #CCC !important;
4501 border-bottom: none !important;
4576 border-bottom: none !important;
4502 vertical-align: middle !important;
4577 vertical-align: middle !important;
4503
4578
4504 }
4579 }
4505 table.code-difftable .lineno.new {
4580 table.code-difftable .lineno.new {
4506 }
4581 }
4507 table.code-difftable .lineno.old {
4582 table.code-difftable .lineno.old {
4508 }
4583 }
4509 table.code-difftable .lineno a{
4584 table.code-difftable .lineno a{
4510 color:#747474 !important;
4585 color:#747474 !important;
4511 font:11px "Bitstream Vera Sans Mono",Monaco,"Courier New",Courier,monospace !important;
4586 font:11px "Bitstream Vera Sans Mono",Monaco,"Courier New",Courier,monospace !important;
4512 letter-spacing:-1px;
4587 letter-spacing:-1px;
4513 text-align:right;
4588 text-align:right;
4514 padding-right: 2px;
4589 padding-right: 2px;
4515 cursor: pointer;
4590 cursor: pointer;
4516 display: block;
4591 display: block;
4517 width: 32px;
4592 width: 32px;
4518 }
4593 }
4519
4594
4520 table.code-difftable .lineno-inline{
4595 table.code-difftable .lineno-inline{
4521 background:none repeat scroll 0 0 #FFF !important;
4596 background:none repeat scroll 0 0 #FFF !important;
4522 padding-left:2px;
4597 padding-left:2px;
4523 padding-right:2px;
4598 padding-right:2px;
4524 text-align:right;
4599 text-align:right;
4525 width:30px;
4600 width:30px;
4526 -moz-user-select:none;
4601 -moz-user-select:none;
4527 -webkit-user-select: none;
4602 -webkit-user-select: none;
4528 }
4603 }
4529
4604
4530 /** CODE **/
4605 /** CODE **/
4531 table.code-difftable .code {
4606 table.code-difftable .code {
4532 display: block;
4607 display: block;
4533 width: 100%;
4608 width: 100%;
4534 }
4609 }
4535 table.code-difftable .code td{
4610 table.code-difftable .code td{
4536 margin:0;
4611 margin:0;
4537 padding:0;
4612 padding:0;
4538 }
4613 }
4539 table.code-difftable .code pre{
4614 table.code-difftable .code pre{
4540 margin:0;
4615 margin:0;
4541 padding:0;
4616 padding:0;
4542 height: 17px;
4617 height: 17px;
4543 line-height: 17px;
4618 line-height: 17px;
4544 }
4619 }
4545
4620
4546
4621
4547 .diffblock.margined.comm .line .code:hover{
4622 .diffblock.margined.comm .line .code:hover{
4548 background-color:#FFFFCC !important;
4623 background-color:#FFFFCC !important;
4549 cursor: pointer !important;
4624 cursor: pointer !important;
4550 background-image:url("../images/icons/comment_add.png") !important;
4625 background-image:url("../images/icons/comment_add.png") !important;
4551 background-repeat:no-repeat !important;
4626 background-repeat:no-repeat !important;
4552 background-position: right !important;
4627 background-position: right !important;
4553 background-position: 0% 50% !important;
4628 background-position: 0% 50% !important;
4554 }
4629 }
4555 .diffblock.margined.comm .line .code.no-comment:hover{
4630 .diffblock.margined.comm .line .code.no-comment:hover{
4556 background-image: none !important;
4631 background-image: none !important;
4557 cursor: auto !important;
4632 cursor: auto !important;
4558 background-color: inherit !important;
4633 background-color: inherit !important;
4559
4634
4560 }
4635 }
@@ -1,249 +1,306 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Settings administration')} - ${c.rhodecode_name}
5 ${_('Settings administration')} - ${c.rhodecode_name}
6 </%def>
6 </%def>
7
7
8 <%def name="breadcrumbs_links()">
8 <%def name="breadcrumbs_links()">
9 ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; ${_('Settings')}
9 ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; ${_('Settings')}
10 </%def>
10 </%def>
11
11
12 <%def name="page_nav()">
12 <%def name="page_nav()">
13 ${self.menu('admin')}
13 ${self.menu('admin')}
14 </%def>
14 </%def>
15
15
16 <%def name="main()">
16 <%def name="main()">
17 <div class="box">
17 <div class="box">
18 <!-- box / title -->
18 <!-- box / title -->
19 <div class="title">
19 <div class="title">
20 ${self.breadcrumbs()}
20 ${self.breadcrumbs()}
21 </div>
21 </div>
22 <!-- end box / title -->
22 <!-- end box / title -->
23
23
24 <h3>${_('Remap and rescan repositories')}</h3>
24 <h3>${_('Remap and rescan repositories')}</h3>
25 ${h.form(url('admin_setting', setting_id='mapping'),method='put')}
25 ${h.form(url('admin_setting', setting_id='mapping'),method='put')}
26 <div class="form">
26 <div class="form">
27 <!-- fields -->
27 <!-- fields -->
28
28
29 <div class="fields">
29 <div class="fields">
30 <div class="field">
30 <div class="field">
31 <div class="label label-checkbox">
31 <div class="label label-checkbox">
32 <label for="destroy">${_('rescan option')}:</label>
32 <label for="destroy">${_('rescan option')}:</label>
33 </div>
33 </div>
34 <div class="checkboxes">
34 <div class="checkboxes">
35 <div class="checkbox">
35 <div class="checkbox">
36 ${h.checkbox('destroy',True)}
36 ${h.checkbox('destroy',True)}
37 <label for="destroy">
37 <label for="destroy">
38 <span class="tooltip" title="${h.tooltip(_('In case a repository was deleted from filesystem and there are leftovers in the database check this option to scan obsolete data in database and remove it.'))}">
38 <span class="tooltip" title="${h.tooltip(_('In case a repository was deleted from filesystem and there are leftovers in the database check this option to scan obsolete data in database and remove it.'))}">
39 ${_('destroy old data')}</span> </label>
39 ${_('destroy old data')}</span> </label>
40 </div>
40 </div>
41 <span class="help-block">${_('Rescan repositories location for new repositories. Also deletes obsolete if `destroy` flag is checked ')}</span>
41 <span class="help-block">${_('Rescan repositories location for new repositories. Also deletes obsolete if `destroy` flag is checked ')}</span>
42 </div>
42 </div>
43 </div>
43 </div>
44
44
45 <div class="buttons">
45 <div class="buttons">
46 ${h.submit('rescan',_('Rescan repositories'),class_="ui-btn large")}
46 ${h.submit('rescan',_('Rescan repositories'),class_="ui-btn large")}
47 </div>
47 </div>
48 </div>
48 </div>
49 </div>
49 </div>
50 ${h.end_form()}
50 ${h.end_form()}
51
51
52 <h3>${_('Whoosh indexing')}</h3>
52 <h3>${_('Whoosh indexing')}</h3>
53 ${h.form(url('admin_setting', setting_id='whoosh'),method='put')}
53 ${h.form(url('admin_setting', setting_id='whoosh'),method='put')}
54 <div class="form">
54 <div class="form">
55 <!-- fields -->
55 <!-- fields -->
56
56
57 <div class="fields">
57 <div class="fields">
58 <div class="field">
58 <div class="field">
59 <div class="label label-checkbox">
59 <div class="label label-checkbox">
60 <label>${_('index build option')}:</label>
60 <label>${_('index build option')}:</label>
61 </div>
61 </div>
62 <div class="checkboxes">
62 <div class="checkboxes">
63 <div class="checkbox">
63 <div class="checkbox">
64 ${h.checkbox('full_index',True)}
64 ${h.checkbox('full_index',True)}
65 <label for="full_index">${_('build from scratch')}</label>
65 <label for="full_index">${_('build from scratch')}</label>
66 </div>
66 </div>
67 </div>
67 </div>
68 </div>
68 </div>
69
69
70 <div class="buttons">
70 <div class="buttons">
71 ${h.submit('reindex',_('Reindex'),class_="ui-btn large")}
71 ${h.submit('reindex',_('Reindex'),class_="ui-btn large")}
72 </div>
72 </div>
73 </div>
73 </div>
74 </div>
74 </div>
75 ${h.end_form()}
75 ${h.end_form()}
76
76
77 <h3>${_('Global application settings')}</h3>
77 <h3>${_('Global application settings')}</h3>
78 ${h.form(url('admin_setting', setting_id='global'),method='put')}
78 ${h.form(url('admin_setting', setting_id='global'),method='put')}
79 <div class="form">
79 <div class="form">
80 <!-- fields -->
80 <!-- fields -->
81
81
82 <div class="fields">
82 <div class="fields">
83
83
84 <div class="field">
84 <div class="field">
85 <div class="label">
85 <div class="label">
86 <label for="rhodecode_title">${_('Application name')}:</label>
86 <label for="rhodecode_title">${_('Application name')}:</label>
87 </div>
87 </div>
88 <div class="input">
88 <div class="input">
89 ${h.text('rhodecode_title',size=30)}
89 ${h.text('rhodecode_title',size=30)}
90 </div>
90 </div>
91 </div>
91 </div>
92
92
93 <div class="field">
93 <div class="field">
94 <div class="label">
94 <div class="label">
95 <label for="rhodecode_realm">${_('Realm text')}:</label>
95 <label for="rhodecode_realm">${_('Realm text')}:</label>
96 </div>
96 </div>
97 <div class="input">
97 <div class="input">
98 ${h.text('rhodecode_realm',size=30)}
98 ${h.text('rhodecode_realm',size=30)}
99 </div>
99 </div>
100 </div>
100 </div>
101
101
102 <div class="field">
102 <div class="field">
103 <div class="label">
103 <div class="label">
104 <label for="rhodecode_ga_code">${_('GA code')}:</label>
104 <label for="rhodecode_ga_code">${_('GA code')}:</label>
105 </div>
105 </div>
106 <div class="input">
106 <div class="input">
107 ${h.text('rhodecode_ga_code',size=30)}
107 ${h.text('rhodecode_ga_code',size=30)}
108 </div>
108 </div>
109 </div>
109 </div>
110
110
111 <div class="buttons">
111 <div class="buttons">
112 ${h.submit('save',_('Save settings'),class_="ui-btn large")}
112 ${h.submit('save',_('Save settings'),class_="ui-btn large")}
113 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
113 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
114 </div>
114 </div>
115 </div>
115 </div>
116 </div>
116 </div>
117 ${h.end_form()}
117 ${h.end_form()}
118
118
119 <h3>${_('Visualisation settings')}</h3>
120 ${h.form(url('admin_setting', setting_id='visual'),method='put')}
121 <div class="form">
122 <!-- fields -->
123
124 <div class="fields">
125
126 <div class="field">
127 <div class="label label-checkbox">
128 <label>${_('Icons')}:</label>
129 </div>
130 <div class="checkboxes">
131 <div class="checkbox">
132 ${h.checkbox('rhodecode_show_public_icon','True')}
133 <label for="rhodecode_show_public_icon">${_('Show public repo icon on repositories')}</label>
134 </div>
135 <div class="checkbox">
136 ${h.checkbox('rhodecode_show_private_icon','True')}
137 <label for="rhodecode_show_private_icon">${_('Show private repo icon on repositories')}</label>
138 </div>
139 </div>
140 </div>
141
142 <div class="field">
143 <div class="label label-checkbox">
144 <label>${_('Meta-Tagging')}:</label>
145 </div>
146 <div class="checkboxes">
147 <div class="checkbox">
148 ${h.checkbox('rhodecode_stylify_metatags','True')}
149 <label for="rhodecode_stylify_metatags">${_('Stylify recognised metatags:')}</label>
150 </div>
151 <div style="padding-left: 20px;">
152 <ul> <!-- Fix style here -->
153 <li>[featured] <span class="metatag" tag="featured">featured</span></li>
154 <li>[stale] <span class="metatag" tag="stale">stale</span></li>
155 <li>[dead] <span class="metatag" tag="dead">dead</span></li>
156 <li>[lang =&gt; lang] <span class="metatag" tag="lang" >lang</span></li>
157 <li>[license =&gt; License] <span class="metatag" tag="license"><a href="http://www.opensource.org/licenses/License" >License</a></span></li>
158 <li>[requires =&gt; Repo] <span class="metatag" tag="requires" >requires =&gt; <a href="#" >Repo</a></span></li>
159 <li>[recommends =&gt; Repo] <span class="metatag" tag="recommends" >recommends =&gt; <a href="#" >Repo</a></span></li>
160 <li>[see =&gt; URI] <span class="metatag" tag="see">see =&gt; <a href="#">URI</a> </span></li>
161 </ul>
162 </div>
163 </div>
164 </div>
165
166 <div class="buttons">
167 ${h.submit('save',_('Save settings'),class_="ui-btn large")}
168 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
169 </div>
170
171 </div>
172 </div>
173 ${h.end_form()}
174
175
119 <h3>${_('VCS settings')}</h3>
176 <h3>${_('VCS settings')}</h3>
120 ${h.form(url('admin_setting', setting_id='vcs'),method='put')}
177 ${h.form(url('admin_setting', setting_id='vcs'),method='put')}
121 <div class="form">
178 <div class="form">
122 <!-- fields -->
179 <!-- fields -->
123
180
124 <div class="fields">
181 <div class="fields">
125
182
126 <div class="field">
183 <div class="field">
127 <div class="label label-checkbox">
184 <div class="label label-checkbox">
128 <label>${_('Web')}:</label>
185 <label>${_('Web')}:</label>
129 </div>
186 </div>
130 <div class="checkboxes">
187 <div class="checkboxes">
131 <div class="checkbox">
188 <div class="checkbox">
132 ${h.checkbox('web_push_ssl','true')}
189 ${h.checkbox('web_push_ssl','true')}
133 <label for="web_push_ssl">${_('require ssl for vcs operations')}</label>
190 <label for="web_push_ssl">${_('require ssl for vcs operations')}</label>
134 </div>
191 </div>
135 <span class="help-block">${_('RhodeCode will require SSL for pushing or pulling. If SSL is missing it will return HTTP Error 406: Not Acceptable')}</span>
192 <span class="help-block">${_('RhodeCode will require SSL for pushing or pulling. If SSL is missing it will return HTTP Error 406: Not Acceptable')}</span>
136 </div>
193 </div>
137 </div>
194 </div>
138
195
139 <div class="field">
196 <div class="field">
140 <div class="label label-checkbox">
197 <div class="label label-checkbox">
141 <label>${_('Hooks')}:</label>
198 <label>${_('Hooks')}:</label>
142 </div>
199 </div>
143 <div class="checkboxes">
200 <div class="checkboxes">
144 <div class="checkbox">
201 <div class="checkbox">
145 ${h.checkbox('hooks_changegroup_update','True')}
202 ${h.checkbox('hooks_changegroup_update','True')}
146 <label for="hooks_changegroup_update">${_('Update repository after push (hg update)')}</label>
203 <label for="hooks_changegroup_update">${_('Update repository after push (hg update)')}</label>
147 </div>
204 </div>
148 <div class="checkbox">
205 <div class="checkbox">
149 ${h.checkbox('hooks_changegroup_repo_size','True')}
206 ${h.checkbox('hooks_changegroup_repo_size','True')}
150 <label for="hooks_changegroup_repo_size">${_('Show repository size after push')}</label>
207 <label for="hooks_changegroup_repo_size">${_('Show repository size after push')}</label>
151 </div>
208 </div>
152 <div class="checkbox">
209 <div class="checkbox">
153 ${h.checkbox('hooks_changegroup_push_logger','True')}
210 ${h.checkbox('hooks_changegroup_push_logger','True')}
154 <label for="hooks_changegroup_push_logger">${_('Log user push commands')}</label>
211 <label for="hooks_changegroup_push_logger">${_('Log user push commands')}</label>
155 </div>
212 </div>
156 <div class="checkbox">
213 <div class="checkbox">
157 ${h.checkbox('hooks_preoutgoing_pull_logger','True')}
214 ${h.checkbox('hooks_preoutgoing_pull_logger','True')}
158 <label for="hooks_preoutgoing_pull_logger">${_('Log user pull commands')}</label>
215 <label for="hooks_preoutgoing_pull_logger">${_('Log user pull commands')}</label>
159 </div>
216 </div>
160 </div>
217 </div>
161 <div class="input" style="margin-top:10px">
218 <div class="input" style="margin-top:10px">
162 ${h.link_to(_('advanced setup'),url('admin_edit_setting',setting_id='hooks'),class_="ui-btn")}
219 ${h.link_to(_('advanced setup'),url('admin_edit_setting',setting_id='hooks'),class_="ui-btn")}
163 </div>
220 </div>
164 </div>
221 </div>
165 <div class="field">
222 <div class="field">
166 <div class="label">
223 <div class="label">
167 <label for="paths_root_path">${_('Repositories location')}:</label>
224 <label for="paths_root_path">${_('Repositories location')}:</label>
168 </div>
225 </div>
169 <div class="input">
226 <div class="input">
170 ${h.text('paths_root_path',size=30,readonly="readonly")}
227 ${h.text('paths_root_path',size=30,readonly="readonly")}
171 <span id="path_unlock" class="tooltip"
228 <span id="path_unlock" class="tooltip"
172 title="${h.tooltip(_('This a crucial application setting. If you are really sure you need to change this, you must restart application in order to make this setting take effect. Click this label to unlock.'))}">
229 title="${h.tooltip(_('This a crucial application setting. If you are really sure you need to change this, you must restart application in order to make this setting take effect. Click this label to unlock.'))}">
173 ${_('unlock')}</span>
230 ${_('unlock')}</span>
174 <span class="help-block">${_('Location where repositories are stored. After changing this value a restart, and rescan is required')}</span>
231 <span class="help-block">${_('Location where repositories are stored. After changing this value a restart, and rescan is required')}</span>
175 </div>
232 </div>
176 </div>
233 </div>
177
234
178 <div class="buttons">
235 <div class="buttons">
179 ${h.submit('save',_('Save settings'),class_="ui-btn large")}
236 ${h.submit('save',_('Save settings'),class_="ui-btn large")}
180 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
237 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
181 </div>
238 </div>
182 </div>
239 </div>
183 </div>
240 </div>
184 ${h.end_form()}
241 ${h.end_form()}
185
242
186 <script type="text/javascript">
243 <script type="text/javascript">
187 YAHOO.util.Event.onDOMReady(function(){
244 YAHOO.util.Event.onDOMReady(function(){
188 YAHOO.util.Event.addListener('path_unlock','click',function(){
245 YAHOO.util.Event.addListener('path_unlock','click',function(){
189 YAHOO.util.Dom.get('paths_root_path').removeAttribute('readonly');
246 YAHOO.util.Dom.get('paths_root_path').removeAttribute('readonly');
190 });
247 });
191 });
248 });
192 </script>
249 </script>
193
250
194 <h3>${_('Test Email')}</h3>
251 <h3>${_('Test Email')}</h3>
195 ${h.form(url('admin_setting', setting_id='email'),method='put')}
252 ${h.form(url('admin_setting', setting_id='email'),method='put')}
196 <div class="form">
253 <div class="form">
197 <!-- fields -->
254 <!-- fields -->
198
255
199 <div class="fields">
256 <div class="fields">
200 <div class="field">
257 <div class="field">
201 <div class="label">
258 <div class="label">
202 <label for="test_email">${_('Email to')}:</label>
259 <label for="test_email">${_('Email to')}:</label>
203 </div>
260 </div>
204 <div class="input">
261 <div class="input">
205 ${h.text('test_email',size=30)}
262 ${h.text('test_email',size=30)}
206 </div>
263 </div>
207 </div>
264 </div>
208
265
209 <div class="buttons">
266 <div class="buttons">
210 ${h.submit('send',_('Send'),class_="ui-btn large")}
267 ${h.submit('send',_('Send'),class_="ui-btn large")}
211 </div>
268 </div>
212 </div>
269 </div>
213 </div>
270 </div>
214 ${h.end_form()}
271 ${h.end_form()}
215
272
216 <h3>${_('System Info and Packages')}</h3>
273 <h3>${_('System Info and Packages')}</h3>
217 <div class="form">
274 <div class="form">
218 <div>
275 <div>
219 <h5 id="expand_modules" style="cursor: pointer">&darr; ${_('show')} &darr;</h5>
276 <h5 id="expand_modules" style="cursor: pointer">&darr; ${_('show')} &darr;</h5>
220 </div>
277 </div>
221 <div id="expand_modules_table" style="display:none">
278 <div id="expand_modules_table" style="display:none">
222 <h5>Python - ${c.py_version}</h5>
279 <h5>Python - ${c.py_version}</h5>
223 <h5>System - ${c.platform}</h5>
280 <h5>System - ${c.platform}</h5>
224
281
225 <table class="table" style="margin:0px 0px 0px 20px">
282 <table class="table" style="margin:0px 0px 0px 20px">
226 <colgroup>
283 <colgroup>
227 <col style="width:220px">
284 <col style="width:220px">
228 </colgroup>
285 </colgroup>
229 <tbody>
286 <tbody>
230 %for key, value in c.modules:
287 %for key, value in c.modules:
231 <tr>
288 <tr>
232 <th style="text-align: right;padding-right:5px;">${key}</th>
289 <th style="text-align: right;padding-right:5px;">${key}</th>
233 <td>${value}</td>
290 <td>${value}</td>
234 </tr>
291 </tr>
235 %endfor
292 %endfor
236 </tbody>
293 </tbody>
237 </table>
294 </table>
238 </div>
295 </div>
239 </div>
296 </div>
240
297
241 <script type="text/javascript">
298 <script type="text/javascript">
242 YUE.on('expand_modules','click',function(e){
299 YUE.on('expand_modules','click',function(e){
243 YUD.setStyle('expand_modules_table','display','');
300 YUD.setStyle('expand_modules_table','display','');
244 YUD.setStyle('expand_modules','display','none');
301 YUD.setStyle('expand_modules','display','none');
245 })
302 })
246 </script>
303 </script>
247
304
248 </div>
305 </div>
249 </%def>
306 </%def>
@@ -1,111 +1,110 b''
1 ## DATA TABLE RE USABLE ELEMENTS
1 ## DATA TABLE RE USABLE ELEMENTS
2 ## usage:
2 ## usage:
3 ## <%namespace name="dt" file="/data_table/_dt_elements.html"/>
3 ## <%namespace name="dt" file="/data_table/_dt_elements.html"/>
4
4
5 <%def name="repo_actions(repo_name)">
5 <%def name="repo_actions(repo_name)">
6 ${h.form(h.url('repo', repo_name=repo_name),method='delete')}
6 ${h.form(h.url('repo', repo_name=repo_name),method='delete')}
7 ${h.submit('remove_%s' % repo_name,_('delete'),class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this repository: %s') % repo_name+"');")}
7 ${h.submit('remove_%s' % repo_name,_('delete'),class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this repository: %s') % repo_name+"');")}
8 ${h.end_form()}
8 ${h.end_form()}
9 </%def>
9 </%def>
10
10
11 <%def name="quick_menu(repo_name)">
11 <%def name="quick_menu(repo_name)">
12 <ul class="menu_items hidden">
12 <ul class="menu_items hidden">
13 <li style="border-top:1px solid #003367;margin-left:18px;padding-left:-99px"></li>
13 <li style="border-top:1px solid #003367;margin-left:18px;padding-left:-99px"></li>
14 <li>
14 <li>
15 <a title="${_('Summary')}" href="${h.url('summary_home',repo_name=repo_name)}">
15 <a title="${_('Summary')}" href="${h.url('summary_home',repo_name=repo_name)}">
16 <span class="icon">
16 <span class="icon">
17 <img src="${h.url('/images/icons/clipboard_16.png')}" alt="${_('Summary')}" />
17 <img src="${h.url('/images/icons/clipboard_16.png')}" alt="${_('Summary')}" />
18 </span>
18 </span>
19 <span>${_('Summary')}</span>
19 <span>${_('Summary')}</span>
20 </a>
20 </a>
21 </li>
21 </li>
22 <li>
22 <li>
23 <a title="${_('Changelog')}" href="${h.url('changelog_home',repo_name=repo_name)}">
23 <a title="${_('Changelog')}" href="${h.url('changelog_home',repo_name=repo_name)}">
24 <span class="icon">
24 <span class="icon">
25 <img src="${h.url('/images/icons/time.png')}" alt="${_('Changelog')}" />
25 <img src="${h.url('/images/icons/time.png')}" alt="${_('Changelog')}" />
26 </span>
26 </span>
27 <span>${_('Changelog')}</span>
27 <span>${_('Changelog')}</span>
28 </a>
28 </a>
29 </li>
29 </li>
30 <li>
30 <li>
31 <a title="${_('Files')}" href="${h.url('files_home',repo_name=repo_name)}">
31 <a title="${_('Files')}" href="${h.url('files_home',repo_name=repo_name)}">
32 <span class="icon">
32 <span class="icon">
33 <img src="${h.url('/images/icons/file.png')}" alt="${_('Files')}" />
33 <img src="${h.url('/images/icons/file.png')}" alt="${_('Files')}" />
34 </span>
34 </span>
35 <span>${_('Files')}</span>
35 <span>${_('Files')}</span>
36 </a>
36 </a>
37 </li>
37 </li>
38 <li>
38 <li>
39 <a title="${_('Fork')}" href="${h.url('repo_fork_home',repo_name=repo_name)}">
39 <a title="${_('Fork')}" href="${h.url('repo_fork_home',repo_name=repo_name)}">
40 <span class="icon">
40 <span class="icon">
41 <img src="${h.url('/images/icons/arrow_divide.png')}" alt="${_('Fork')}" />
41 <img src="${h.url('/images/icons/arrow_divide.png')}" alt="${_('Fork')}" />
42 </span>
42 </span>
43 <span>${_('Fork')}</span>
43 <span>${_('Fork')}</span>
44 </a>
44 </a>
45 </li>
45 </li>
46 </ul>
46 </ul>
47 </%def>
47 </%def>
48
48
49 <%def name="repo_name(name,rtype,private,fork_of,short_name=False, admin=False)">
49 <%def name="repo_name(name,rtype,private,fork_of,short_name=False, admin=False)">
50 <%
50 <%
51 def get_name(name,short_name=short_name):
51 def get_name(name,short_name=short_name):
52 if short_name:
52 if short_name:
53 return name.split('/')[-1]
53 return name.split('/')[-1]
54 else:
54 else:
55 return name
55 return name
56 %>
56 %>
57 <div style="white-space: nowrap">
57 <div style="white-space: nowrap">
58 ##TYPE OF REPO
58 ##TYPE OF REPO
59 %if h.is_hg(rtype):
59 %if h.is_hg(rtype):
60 <img class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url('/images/icons/hgicon.png')}"/>
60 <img class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url('/images/icons/hgicon.png')}"/>
61 %elif h.is_git(rtype):
61 %elif h.is_git(rtype):
62 <img class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url('/images/icons/giticon.png')}"/>
62 <img class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url('/images/icons/giticon.png')}"/>
63 %endif
63 %endif
64
64
65 ##PRIVATE/PUBLIC
65 ##PRIVATE/PUBLIC
66 %if private:
66 %if private and c.visual.show_private_icon:
67 <img class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="${h.url('/images/icons/lock.png')}"/>
67 <img class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="${h.url('/images/icons/lock.png')}"/>
68 %else:
68 %elif not private and c.visual.show_public_icon:
69 <img class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="${h.url('/images/icons/lock_open.png')}"/>
69 <img class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="${h.url('/images/icons/lock_open.png')}"/>
70 %endif
70 %endif
71
71
72 ##NAME
72 ##NAME
73 %if admin:
73 %if admin:
74 ${h.link_to(get_name(name),h.url('edit_repo',repo_name=name),class_="repo_name")}
74 ${h.link_to(get_name(name),h.url('edit_repo',repo_name=name),class_="repo_name")}
75 %else:
75 %else:
76 ${h.link_to(get_name(name),h.url('summary_home',repo_name=name),class_="repo_name")}
76 ${h.link_to(get_name(name),h.url('summary_home',repo_name=name),class_="repo_name")}
77 %endif
77 %endif
78 %if fork_of:
78 %if fork_of:
79 <a href="${h.url('summary_home',repo_name=fork_of)}">
79 <a href="${h.url('summary_home',repo_name=fork_of)}">
80 <img class="icon" alt="${_('fork')}" title="${_('Fork of')} ${fork_of}" src="${h.url('/images/icons/arrow_divide.png')}"/></a>
80 <img class="icon" alt="${_('fork')}" title="${_('Fork of')} ${fork_of}" src="${h.url('/images/icons/arrow_divide.png')}"/></a>
81 %endif
81 %endif
82 </div>
82 </div>
83 </%def>
83 </%def>
84
84
85
85
86
86
87 <%def name="revision(name,rev,tip,author,last_msg)">
87 <%def name="revision(name,rev,tip,author,last_msg)">
88 <div>
88 <div>
89 %if rev >= 0:
89 %if rev >= 0:
90 <pre><a title="${h.tooltip('%s:\n\n%s' % (author,last_msg))}" class="tooltip" href="${h.url('changeset_home',repo_name=name,revision=tip)}">${'r%s:%s' % (rev,h.short_id(tip))}</a></pre>
90 <pre><a title="${h.tooltip('%s:\n\n%s' % (author,last_msg))}" class="tooltip" href="${h.url('changeset_home',repo_name=name,revision=tip)}">${'r%s:%s' % (rev,h.short_id(tip))}</a></pre>
91 %else:
91 %else:
92 ${_('No changesets yet')}
92 ${_('No changesets yet')}
93 %endif
93 %endif
94 </div>
94 </div>
95 </%def>
95 </%def>
96
96
97 <%def name="user_gravatar(email, size=24)">
97 <%def name="user_gravatar(email, size=24)">
98 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(email, size)}"/> </div>
98 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(email, size)}"/> </div>
99 </%def>
99 </%def>
100
100
101 <%def name="user_actions(user_id, username)">
101 <%def name="user_actions(user_id, username)">
102 ${h.form(h.url('delete_user', id=user_id),method='delete')}
102 ${h.form(h.url('delete_user', id=user_id),method='delete')}
103 ${h.submit('remove_',_('delete'),id="remove_user_%s" % user_id,
103 ${h.submit('remove_',_('delete'),id="remove_user_%s" % user_id,
104 class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this user: %s') % username+"');")}
104 class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this user: %s') % username+"');")}
105 ${h.end_form()}
105 ${h.end_form()}
106 </%def>
106 </%def>
107
107
108 <%def name="user_name(user_id, username)">
108 <%def name="user_name(user_id, username)">
109 ${h.link_to(username,h.url('edit_user', id=user_id))}
109 ${h.link_to(username,h.url('edit_user', id=user_id))}
110 </%def>
110 </%def>
111
@@ -1,201 +1,209 b''
1 <%page args="parent" />
1 <%page args="parent" />
2 <div class="box">
2 <div class="box">
3 <!-- box / title -->
3 <!-- box / title -->
4 <div class="title">
4 <div class="title">
5 <h5>
5 <h5>
6 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" value="${_('quick filter...')}"/> ${parent.breadcrumbs()} <span id="repo_count">0</span> ${_('repositories')}
6 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" value="${_('quick filter...')}"/> ${parent.breadcrumbs()} <span id="repo_count">0</span> ${_('repositories')}
7 </h5>
7 </h5>
8 %if c.rhodecode_user.username != 'default':
8 %if c.rhodecode_user.username != 'default':
9 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
9 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
10 <ul class="links">
10 <ul class="links">
11 <li>
11 <li>
12 %if c.group:
12 %if c.group:
13 <span>${h.link_to(_('ADD REPOSITORY'),h.url('admin_settings_create_repository',parent_group=c.group.group_id))}</span>
13 <span>${h.link_to(_('ADD REPOSITORY'),h.url('admin_settings_create_repository',parent_group=c.group.group_id))}</span>
14 %else:
14 %else:
15 <span>${h.link_to(_('ADD REPOSITORY'),h.url('admin_settings_create_repository'))}</span>
15 <span>${h.link_to(_('ADD REPOSITORY'),h.url('admin_settings_create_repository'))}</span>
16 %endif
16 %endif
17 </li>
17 </li>
18 </ul>
18 </ul>
19 %endif
19 %endif
20 %endif
20 %endif
21 </div>
21 </div>
22 <!-- end box / title -->
22 <!-- end box / title -->
23 <div class="table">
23 <div class="table">
24 % if c.groups:
24 % if c.groups:
25 <div id='groups_list_wrap' class="yui-skin-sam">
25 <div id='groups_list_wrap' class="yui-skin-sam">
26 <table id="groups_list">
26 <table id="groups_list">
27 <thead>
27 <thead>
28 <tr>
28 <tr>
29 <th class="left"><a href="#">${_('Group name')}</a></th>
29 <th class="left"><a href="#">${_('Group name')}</a></th>
30 <th class="left"><a href="#">${_('Description')}</a></th>
30 <th class="left"><a href="#">${_('Description')}</a></th>
31 ##<th class="left"><a href="#">${_('Number of repositories')}</a></th>
31 ##<th class="left"><a href="#">${_('Number of repositories')}</a></th>
32 </tr>
32 </tr>
33 </thead>
33 </thead>
34
34
35 ## REPO GROUPS
35 ## REPO GROUPS
36 % for gr in c.groups:
36 % for gr in c.groups:
37 <tr>
37 <tr>
38 <td>
38 <td>
39 <div style="white-space: nowrap">
39 <div style="white-space: nowrap">
40 <img class="icon" alt="${_('Repositories group')}" src="${h.url('/images/icons/database_link.png')}"/>
40 <img class="icon" alt="${_('Repositories group')}" src="${h.url('/images/icons/database_link.png')}"/>
41 ${h.link_to(gr.name,url('repos_group_home',group_name=gr.group_name))}
41 ${h.link_to(gr.name,url('repos_group_home',group_name=gr.group_name))}
42 </div>
42 </div>
43 </td>
43 </td>
44 <td>${gr.group_description}</td>
44 %if c.visual.stylify_metatags:
45 <td>${h.desc_stylize(gr.group_description)}</td>
46 %else:
47 <td>${gr.group_description}</td>
48 %endif
45 ## this is commented out since for multi nested repos can be HEAVY!
49 ## this is commented out since for multi nested repos can be HEAVY!
46 ## in number of executed queries during traversing uncomment at will
50 ## in number of executed queries during traversing uncomment at will
47 ##<td><b>${gr.repositories_recursive_count}</b></td>
51 ##<td><b>${gr.repositories_recursive_count}</b></td>
48 </tr>
52 </tr>
49 % endfor
53 % endfor
50
54
51 </table>
55 </table>
52 </div>
56 </div>
53 <div style="height: 20px"></div>
57 <div style="height: 20px"></div>
54 % endif
58 % endif
55 <div id="welcome" style="display:none;text-align:center">
59 <div id="welcome" style="display:none;text-align:center">
56 <h1><a href="${h.url('home')}">${c.rhodecode_name} ${c.rhodecode_version}</a></h1>
60 <h1><a href="${h.url('home')}">${c.rhodecode_name} ${c.rhodecode_version}</a></h1>
57 </div>
61 </div>
58 <div id='repos_list_wrap' class="yui-skin-sam">
62 <div id='repos_list_wrap' class="yui-skin-sam">
59 <%cnt=0%>
63 <%cnt=0%>
60 <%namespace name="dt" file="/data_table/_dt_elements.html"/>
64 <%namespace name="dt" file="/data_table/_dt_elements.html"/>
61
65
62 <table id="repos_list">
66 <table id="repos_list">
63 <thead>
67 <thead>
64 <tr>
68 <tr>
65 <th class="left"></th>
69 <th class="left"></th>
66 <th class="left">${_('Name')}</th>
70 <th class="left">${_('Name')}</th>
67 <th class="left">${_('Description')}</th>
71 <th class="left">${_('Description')}</th>
68 <th class="left">${_('Last change')}</th>
72 <th class="left">${_('Last change')}</th>
69 <th class="left">${_('Tip')}</th>
73 <th class="left">${_('Tip')}</th>
70 <th class="left">${_('Owner')}</th>
74 <th class="left">${_('Owner')}</th>
71 <th class="left">${_('RSS')}</th>
75 <th class="left">${_('RSS')}</th>
72 <th class="left">${_('Atom')}</th>
76 <th class="left">${_('Atom')}</th>
73 </tr>
77 </tr>
74 </thead>
78 </thead>
75 <tbody>
79 <tbody>
76 %for cnt,repo in enumerate(c.repos_list):
80 %for cnt,repo in enumerate(c.repos_list):
77 <tr class="parity${(cnt+1)%2}">
81 <tr class="parity${(cnt+1)%2}">
78 ##QUICK MENU
82 ##QUICK MENU
79 <td class="quick_repo_menu">
83 <td class="quick_repo_menu">
80 ${dt.quick_menu(repo['name'])}
84 ${dt.quick_menu(repo['name'])}
81 </td>
85 </td>
82 ##REPO NAME AND ICONS
86 ##REPO NAME AND ICONS
83 <td class="reponame">
87 <td class="reponame">
84 ${dt.repo_name(repo['name'],repo['dbrepo']['repo_type'],repo['dbrepo']['private'],repo['dbrepo_fork'].get('repo_name'),pageargs.get('short_repo_names'))}
88 ${dt.repo_name(repo['name'],repo['dbrepo']['repo_type'],repo['dbrepo']['private'],repo['dbrepo_fork'].get('repo_name'),pageargs.get('short_repo_names'))}
85 </td>
89 </td>
86 ##DESCRIPTION
90 ##DESCRIPTION
87 <td><span class="tooltip" title="${h.tooltip(repo['description'])}">
91 <td><span class="tooltip" title="${h.tooltip(repo['description'])}">
92 %if c.visual.stylify_metatags:
93 ${h.urlify_text(h.desc_stylize(h.truncate(repo['description'],60)))}</span>
94 %else:
88 ${h.truncate(repo['description'],60)}</span>
95 ${h.truncate(repo['description'],60)}</span>
96 %endif
89 </td>
97 </td>
90 ##LAST CHANGE DATE
98 ##LAST CHANGE DATE
91 <td>
99 <td>
92 <span class="tooltip" date="${repo['last_change']}" title="${h.tooltip(h.fmt_date(repo['last_change']))}">${h.age(repo['last_change'])}</span>
100 <span class="tooltip" date="${repo['last_change']}" title="${h.tooltip(h.fmt_date(repo['last_change']))}">${h.age(repo['last_change'])}</span>
93 </td>
101 </td>
94 ##LAST REVISION
102 ##LAST REVISION
95 <td>
103 <td>
96 ${dt.revision(repo['name'],repo['rev'],repo['tip'],repo['author'],repo['last_msg'])}
104 ${dt.revision(repo['name'],repo['rev'],repo['tip'],repo['author'],repo['last_msg'])}
97 </td>
105 </td>
98 ##
106 ##
99 <td title="${repo['contact']}">${h.person(repo['contact'])}</td>
107 <td title="${repo['contact']}">${h.person(repo['contact'])}</td>
100 <td>
108 <td>
101 %if c.rhodecode_user.username != 'default':
109 %if c.rhodecode_user.username != 'default':
102 <a title="${_('Subscribe to %s rss feed')%repo['name']}" class="rss_icon" href="${h.url('rss_feed_home',repo_name=repo['name'],api_key=c.rhodecode_user.api_key)}"></a>
110 <a title="${_('Subscribe to %s rss feed')%repo['name']}" class="rss_icon" href="${h.url('rss_feed_home',repo_name=repo['name'],api_key=c.rhodecode_user.api_key)}"></a>
103 %else:
111 %else:
104 <a title="${_('Subscribe to %s rss feed')%repo['name']}" class="rss_icon" href="${h.url('rss_feed_home',repo_name=repo['name'])}"></a>
112 <a title="${_('Subscribe to %s rss feed')%repo['name']}" class="rss_icon" href="${h.url('rss_feed_home',repo_name=repo['name'])}"></a>
105 %endif:
113 %endif:
106 </td>
114 </td>
107 <td>
115 <td>
108 %if c.rhodecode_user.username != 'default':
116 %if c.rhodecode_user.username != 'default':
109 <a title="${_('Subscribe to %s atom feed')%repo['name']}" class="atom_icon" href="${h.url('atom_feed_home',repo_name=repo['name'],api_key=c.rhodecode_user.api_key)}"></a>
117 <a title="${_('Subscribe to %s atom feed')%repo['name']}" class="atom_icon" href="${h.url('atom_feed_home',repo_name=repo['name'],api_key=c.rhodecode_user.api_key)}"></a>
110 %else:
118 %else:
111 <a title="${_('Subscribe to %s atom feed')%repo['name']}" class="atom_icon" href="${h.url('atom_feed_home',repo_name=repo['name'])}"></a>
119 <a title="${_('Subscribe to %s atom feed')%repo['name']}" class="atom_icon" href="${h.url('atom_feed_home',repo_name=repo['name'])}"></a>
112 %endif:
120 %endif:
113 </td>
121 </td>
114 </tr>
122 </tr>
115 %endfor
123 %endfor
116 </tbody>
124 </tbody>
117 </table>
125 </table>
118 </div>
126 </div>
119 </div>
127 </div>
120 </div>
128 </div>
121 <script>
129 <script>
122 YUD.get('repo_count').innerHTML = ${cnt+1 if cnt else 0};
130 YUD.get('repo_count').innerHTML = ${cnt+1 if cnt else 0};
123 var func = function(node){
131 var func = function(node){
124 return node.parentNode.parentNode.parentNode.parentNode;
132 return node.parentNode.parentNode.parentNode.parentNode;
125 }
133 }
126
134
127
135
128 // groups table sorting
136 // groups table sorting
129 var myColumnDefs = [
137 var myColumnDefs = [
130 {key:"name",label:"${_('Group Name')}",sortable:true,
138 {key:"name",label:"${_('Group Name')}",sortable:true,
131 sortOptions: { sortFunction: groupNameSort }},
139 sortOptions: { sortFunction: groupNameSort }},
132 {key:"desc",label:"${_('Description')}",sortable:true},
140 {key:"desc",label:"${_('Description')}",sortable:true},
133 ];
141 ];
134
142
135 var myDataSource = new YAHOO.util.DataSource(YUD.get("groups_list"));
143 var myDataSource = new YAHOO.util.DataSource(YUD.get("groups_list"));
136
144
137 myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
145 myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
138 myDataSource.responseSchema = {
146 myDataSource.responseSchema = {
139 fields: [
147 fields: [
140 {key:"name"},
148 {key:"name"},
141 {key:"desc"},
149 {key:"desc"},
142 ]
150 ]
143 };
151 };
144
152
145 var myDataTable = new YAHOO.widget.DataTable("groups_list_wrap", myColumnDefs, myDataSource,
153 var myDataTable = new YAHOO.widget.DataTable("groups_list_wrap", myColumnDefs, myDataSource,
146 {
154 {
147 sortedBy:{key:"name",dir:"asc"},
155 sortedBy:{key:"name",dir:"asc"},
148 MSG_SORTASC:"${_('Click to sort ascending')}",
156 MSG_SORTASC:"${_('Click to sort ascending')}",
149 MSG_SORTDESC:"${_('Click to sort descending')}"
157 MSG_SORTDESC:"${_('Click to sort descending')}"
150 }
158 }
151 );
159 );
152
160
153 // main table sorting
161 // main table sorting
154 var myColumnDefs = [
162 var myColumnDefs = [
155 {key:"menu",label:"",sortable:false,className:"quick_repo_menu hidden"},
163 {key:"menu",label:"",sortable:false,className:"quick_repo_menu hidden"},
156 {key:"name",label:"${_('Name')}",sortable:true,
164 {key:"name",label:"${_('Name')}",sortable:true,
157 sortOptions: { sortFunction: nameSort }},
165 sortOptions: { sortFunction: nameSort }},
158 {key:"desc",label:"${_('Description')}",sortable:true},
166 {key:"desc",label:"${_('Description')}",sortable:true},
159 {key:"last_change",label:"${_('Last Change')}",sortable:true,
167 {key:"last_change",label:"${_('Last Change')}",sortable:true,
160 sortOptions: { sortFunction: ageSort }},
168 sortOptions: { sortFunction: ageSort }},
161 {key:"tip",label:"${_('Tip')}",sortable:true,
169 {key:"tip",label:"${_('Tip')}",sortable:true,
162 sortOptions: { sortFunction: revisionSort }},
170 sortOptions: { sortFunction: revisionSort }},
163 {key:"owner",label:"${_('Owner')}",sortable:true},
171 {key:"owner",label:"${_('Owner')}",sortable:true},
164 {key:"rss",label:"",sortable:false},
172 {key:"rss",label:"",sortable:false},
165 {key:"atom",label:"",sortable:false},
173 {key:"atom",label:"",sortable:false},
166 ];
174 ];
167
175
168 var myDataSource = new YAHOO.util.DataSource(YUD.get("repos_list"));
176 var myDataSource = new YAHOO.util.DataSource(YUD.get("repos_list"));
169
177
170 myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
178 myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
171
179
172 myDataSource.responseSchema = {
180 myDataSource.responseSchema = {
173 fields: [
181 fields: [
174 {key:"menu"},
182 {key:"menu"},
175 {key:"name"},
183 {key:"name"},
176 {key:"desc"},
184 {key:"desc"},
177 {key:"last_change"},
185 {key:"last_change"},
178 {key:"tip"},
186 {key:"tip"},
179 {key:"owner"},
187 {key:"owner"},
180 {key:"rss"},
188 {key:"rss"},
181 {key:"atom"},
189 {key:"atom"},
182 ]
190 ]
183 };
191 };
184
192
185 var myDataTable = new YAHOO.widget.DataTable("repos_list_wrap", myColumnDefs, myDataSource,
193 var myDataTable = new YAHOO.widget.DataTable("repos_list_wrap", myColumnDefs, myDataSource,
186 {
194 {
187 sortedBy:{key:"name",dir:"asc"},
195 sortedBy:{key:"name",dir:"asc"},
188 MSG_SORTASC:"${_('Click to sort ascending')}",
196 MSG_SORTASC:"${_('Click to sort ascending')}",
189 MSG_SORTDESC:"${_('Click to sort descending')}",
197 MSG_SORTDESC:"${_('Click to sort descending')}",
190 MSG_EMPTY:"${_('No records found.')}",
198 MSG_EMPTY:"${_('No records found.')}",
191 MSG_ERROR:"${_('Data error.')}",
199 MSG_ERROR:"${_('Data error.')}",
192 MSG_LOADING:"${_('Loading...')}",
200 MSG_LOADING:"${_('Loading...')}",
193 }
201 }
194 );
202 );
195 myDataTable.subscribe('postRenderEvent',function(oArgs) {
203 myDataTable.subscribe('postRenderEvent',function(oArgs) {
196 tooltip_activate();
204 tooltip_activate();
197 quick_repo_menu();
205 quick_repo_menu();
198 q_filter('q_filter',YUQ('div.table tr td a.repo_name'),func);
206 q_filter('q_filter',YUQ('div.table tr td a.repo_name'),func);
199 });
207 });
200
208
201 </script>
209 </script>
@@ -1,229 +1,229 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3 <%def name="title()">
3 <%def name="title()">
4 ${_('Journal')} - ${c.rhodecode_name}
4 ${_('Journal')} - ${c.rhodecode_name}
5 </%def>
5 </%def>
6 <%def name="breadcrumbs()">
6 <%def name="breadcrumbs()">
7 ${c.rhodecode_name}
7 ${c.rhodecode_name}
8 </%def>
8 </%def>
9 <%def name="page_nav()">
9 <%def name="page_nav()">
10 ${self.menu('home')}
10 ${self.menu('home')}
11 </%def>
11 </%def>
12 <%def name="head_extra()">
12 <%def name="head_extra()">
13 <link href="${h.url('journal_atom', api_key=c.rhodecode_user.api_key)}" rel="alternate" title="${_('ATOM journal feed')}" type="application/atom+xml" />
13 <link href="${h.url('journal_atom', api_key=c.rhodecode_user.api_key)}" rel="alternate" title="${_('ATOM journal feed')}" type="application/atom+xml" />
14 <link href="${h.url('journal_rss', api_key=c.rhodecode_user.api_key)}" rel="alternate" title="${_('RSS journal feed')}" type="application/rss+xml" />
14 <link href="${h.url('journal_rss', api_key=c.rhodecode_user.api_key)}" rel="alternate" title="${_('RSS journal feed')}" type="application/rss+xml" />
15 </%def>
15 </%def>
16 <%def name="main()">
16 <%def name="main()">
17
17
18 <div class="box box-left">
18 <div class="box box-left">
19 <!-- box / title -->
19 <!-- box / title -->
20 <div class="title">
20 <div class="title">
21 <h5>${_('Journal')}</h5>
21 <h5>${_('Journal')}</h5>
22 <ul class="links">
22 <ul class="links">
23 <li>
23 <li>
24 <span><a id="refresh" href="${h.url('journal')}"><img class="icon" title="${_('Refresh')}" alt="${_('Refresh')}" src="${h.url('/images/icons/arrow_refresh.png')}"/></a></span>
24 <span><a id="refresh" href="${h.url('journal')}"><img class="icon" title="${_('Refresh')}" alt="${_('Refresh')}" src="${h.url('/images/icons/arrow_refresh.png')}"/></a></span>
25 </li>
25 </li>
26 <li>
26 <li>
27 <span><a href="${h.url('journal_rss', api_key=c.rhodecode_user.api_key)}"><img class="icon" title="${_('RSS feed')}" alt="${_('RSS feed')}" src="${h.url('/images/icons/rss_16.png')}"/></a></span>
27 <span><a href="${h.url('journal_rss', api_key=c.rhodecode_user.api_key)}"><img class="icon" title="${_('RSS feed')}" alt="${_('RSS feed')}" src="${h.url('/images/icons/rss_16.png')}"/></a></span>
28 </li>
28 </li>
29 <li>
29 <li>
30 <span><a href="${h.url('journal_atom', api_key=c.rhodecode_user.api_key)}"><img class="icon" title="${_('ATOM feed')}" alt="${_('ATOM feed')}" src="${h.url('/images/icons/atom.png')}"/></a></span>
30 <span><a href="${h.url('journal_atom', api_key=c.rhodecode_user.api_key)}"><img class="icon" title="${_('ATOM feed')}" alt="${_('ATOM feed')}" src="${h.url('/images/icons/atom.png')}"/></a></span>
31 </li>
31 </li>
32 </ul>
32 </ul>
33 </div>
33 </div>
34 <div id="journal">${c.journal_data}</div>
34 <div id="journal">${c.journal_data}</div>
35 </div>
35 </div>
36 <div class="box box-right">
36 <div class="box box-right">
37 <!-- box / title -->
37 <!-- box / title -->
38 <div class="title">
38 <div class="title">
39 <h5>
39 <h5>
40 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" value="${_('quick filter...')}"/>
40 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" value="${_('quick filter...')}"/>
41 <a id="show_my" class="link-white" href="#my">${_('My repos')}</a> / <a id="show_watched" class="link-white" href="#watched">${_('Watched')}</a>
41 <a id="show_my" class="link-white" href="#my">${_('My repos')}</a> / <a id="show_watched" class="link-white" href="#watched">${_('Watched')}</a>
42 </h5>
42 </h5>
43 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
43 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
44 <ul class="links">
44 <ul class="links">
45 <li>
45 <li>
46 <span>${h.link_to(_('ADD'),h.url('admin_settings_create_repository'))}</span>
46 <span>${h.link_to(_('ADD'),h.url('admin_settings_create_repository'))}</span>
47 </li>
47 </li>
48 </ul>
48 </ul>
49 %endif
49 %endif
50 </div>
50 </div>
51 <!-- end box / title -->
51 <!-- end box / title -->
52 <div id="my" class="table">
52 <div id="my" class="table">
53 %if c.user_repos:
53 %if c.user_repos:
54 <div id='repos_list_wrap' class="yui-skin-sam">
54 <div id='repos_list_wrap' class="yui-skin-sam">
55 <table id="repos_list">
55 <table id="repos_list">
56 <thead>
56 <thead>
57 <tr>
57 <tr>
58 <th></th>
58 <th></th>
59 <th class="left">${_('Name')}</th>
59 <th class="left">${_('Name')}</th>
60 <th class="left">${_('Revision')}</th>
60 <th class="left">${_('Revision')}</th>
61 <th class="left">${_('Action')}</th>
61 <th class="left">${_('Action')}</th>
62 <th class="left">${_('Action')}</th>
62 <th class="left">${_('Action')}</th>
63 </thead>
63 </thead>
64 <tbody>
64 <tbody>
65 <%namespace name="dt" file="/data_table/_dt_elements.html"/>
65 <%namespace name="dt" file="/data_table/_dt_elements.html"/>
66 %for repo in c.user_repos:
66 %for repo in c.user_repos:
67 <tr>
67 <tr>
68 ##QUICK MENU
68 ##QUICK MENU
69 <td class="quick_repo_menu">
69 <td class="quick_repo_menu">
70 ${dt.quick_menu(repo['name'])}
70 ${dt.quick_menu(repo['name'])}
71 </td>
71 </td>
72 ##REPO NAME AND ICONS
72 ##REPO NAME AND ICONS
73 <td class="reponame">
73 <td class="reponame">
74 ${dt.repo_name(repo['name'],repo['dbrepo']['repo_type'],repo['dbrepo']['private'],repo['dbrepo_fork'].get('repo_name'))}
74 ${dt.repo_name(repo['name'],repo['dbrepo']['repo_type'],repo['dbrepo']['private'],repo['dbrepo_fork'].get('repo_name'))}
75 </td>
75 </td>
76 ##LAST REVISION
76 ##LAST REVISION
77 <td>
77 <td>
78 ${dt.revision(repo['name'],repo['rev'],repo['tip'],repo['author'],repo['last_msg'])}
78 ${dt.revision(repo['name'],repo['rev'],repo['tip'],repo['author'],repo['last_msg'])}
79 </td>
79 </td>
80 ##
80 ##
81 <td><a href="${h.url('repo_settings_home',repo_name=repo['name'])}" title="${_('edit')}"><img class="icon" alt="${_('private')}" src="${h.url('/images/icons/application_form_edit.png')}"/></a></td>
81 <td><a href="${h.url('repo_settings_home',repo_name=repo['name'])}" title="${_('edit')}"><img class="icon" alt="${_('private')}" src="${h.url('/images/icons/application_form_edit.png')}"/></a></td>
82 <td>
82 <td>
83 ${h.form(url('repo_settings_delete', repo_name=repo['name']),method='delete')}
83 ${h.form(url('repo_settings_delete', repo_name=repo['name']),method='delete')}
84 ${h.submit('remove_%s' % repo['name'],'',class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this repository')+"');")}
84 ${h.submit('remove_%s' % repo['name'],'',class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this repository')+"');")}
85 ${h.end_form()}
85 ${h.end_form()}
86 </td>
86 </td>
87 </tr>
87 </tr>
88 %endfor
88 %endfor
89 </tbody>
89 </tbody>
90 </table>
90 </table>
91 </div>
91 </div>
92 %else:
92 %else:
93 <div style="padding:5px 0px 10px 0px;">
93 <div style="padding:5px 0px 10px 0px;">
94 ${_('No repositories yet')}
94 ${_('No repositories yet')}
95 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
95 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
96 ${h.link_to(_('create one now'),h.url('admin_settings_create_repository'),class_="ui-btn")}
96 ${h.link_to(_('create one now'),h.url('admin_settings_create_repository'),class_="ui-btn")}
97 %endif
97 %endif
98 </div>
98 </div>
99 %endif
99 %endif
100 </div>
100 </div>
101
101
102 <div id="watched" class="table" style="display:none">
102 <div id="watched" class="table" style="display:none">
103 %if c.following:
103 %if c.following:
104 <table>
104 <table>
105 <thead>
105 <thead>
106 <tr>
106 <tr>
107 <th class="left">${_('Name')}</th>
107 <th class="left">${_('Name')}</th>
108 </thead>
108 </thead>
109 <tbody>
109 <tbody>
110 %for entry in c.following:
110 %for entry in c.following:
111 <tr>
111 <tr>
112 <td>
112 <td>
113 %if entry.follows_user_id:
113 %if entry.follows_user_id:
114 <img title="${_('following user')}" alt="${_('user')}" src="${h.url('/images/icons/user.png')}"/>
114 <img title="${_('following user')}" alt="${_('user')}" src="${h.url('/images/icons/user.png')}"/>
115 ${entry.follows_user.full_contact}
115 ${entry.follows_user.full_contact}
116 %endif
116 %endif
117
117
118 %if entry.follows_repo_id:
118 %if entry.follows_repo_id:
119 <div style="float:right;padding-right:5px">
119 <div style="float:right;padding-right:5px">
120 <span id="follow_toggle_${entry.follows_repository.repo_id}" class="following" title="${_('Stop following this repository')}"
120 <span id="follow_toggle_${entry.follows_repository.repo_id}" class="following" title="${_('Stop following this repository')}"
121 onclick="javascript:toggleFollowingRepo(this,${entry.follows_repository.repo_id},'${str(h.get_token())}')">
121 onclick="javascript:toggleFollowingRepo(this,${entry.follows_repository.repo_id},'${str(h.get_token())}')">
122 </span>
122 </span>
123 </div>
123 </div>
124
124
125 %if h.is_hg(entry.follows_repository):
125 %if h.is_hg(entry.follows_repository):
126 <img class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url('/images/icons/hgicon.png')}"/>
126 <img class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url('/images/icons/hgicon.png')}"/>
127 %elif h.is_git(entry.follows_repository):
127 %elif h.is_git(entry.follows_repository):
128 <img class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url('/images/icons/giticon.png')}"/>
128 <img class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url('/images/icons/giticon.png')}"/>
129 %endif
129 %endif
130
130
131 %if entry.follows_repository.private:
131 %if entry.follows_repository.private and c.visual.show_private_icon:
132 <img class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="${h.url('/images/icons/lock.png')}"/>
132 <img class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="${h.url('/images/icons/lock.png')}"/>
133 %else:
133 %elif not entry.follows_repository.private and c.visual.show_public_icon:
134 <img class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="${h.url('/images/icons/lock_open.png')}"/>
134 <img class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="${h.url('/images/icons/lock_open.png')}"/>
135 %endif
135 %endif
136 <span class="watched_repo">
136 <span class="watched_repo">
137 ${h.link_to(entry.follows_repository.repo_name,h.url('summary_home',repo_name=entry.follows_repository.repo_name))}
137 ${h.link_to(entry.follows_repository.repo_name,h.url('summary_home',repo_name=entry.follows_repository.repo_name))}
138 </span>
138 </span>
139 %endif
139 %endif
140 </td>
140 </td>
141 </tr>
141 </tr>
142 %endfor
142 %endfor
143 </tbody>
143 </tbody>
144 </table>
144 </table>
145 %else:
145 %else:
146 <div style="padding:5px 0px 10px 0px;">
146 <div style="padding:5px 0px 10px 0px;">
147 ${_('You are not following any users or repositories')}
147 ${_('You are not following any users or repositories')}
148 </div>
148 </div>
149 %endif
149 %endif
150 </div>
150 </div>
151 </div>
151 </div>
152
152
153 <script type="text/javascript">
153 <script type="text/javascript">
154
154
155 YUE.on('show_my','click',function(e){
155 YUE.on('show_my','click',function(e){
156 YUD.setStyle('watched','display','none');
156 YUD.setStyle('watched','display','none');
157 YUD.setStyle('my','display','');
157 YUD.setStyle('my','display','');
158 var nodes = YUQ('#my tr td a.repo_name');
158 var nodes = YUQ('#my tr td a.repo_name');
159 var target = 'q_filter';
159 var target = 'q_filter';
160 var func = function(node){
160 var func = function(node){
161 return node.parentNode.parentNode.parentNode.parentNode;
161 return node.parentNode.parentNode.parentNode.parentNode;
162 }
162 }
163 q_filter(target,nodes,func);
163 q_filter(target,nodes,func);
164 YUE.preventDefault(e);
164 YUE.preventDefault(e);
165 })
165 })
166 YUE.on('show_watched','click',function(e){
166 YUE.on('show_watched','click',function(e){
167 YUD.setStyle('my','display','none');
167 YUD.setStyle('my','display','none');
168 YUD.setStyle('watched','display','');
168 YUD.setStyle('watched','display','');
169 var nodes = YUQ('#watched .watched_repo a');
169 var nodes = YUQ('#watched .watched_repo a');
170 var target = 'q_filter';
170 var target = 'q_filter';
171 var func = function(node){
171 var func = function(node){
172 return node.parentNode.parentNode;
172 return node.parentNode.parentNode;
173 }
173 }
174 q_filter(target,nodes,func);
174 q_filter(target,nodes,func);
175 YUE.preventDefault(e);
175 YUE.preventDefault(e);
176 })
176 })
177 YUE.on('refresh','click',function(e){
177 YUE.on('refresh','click',function(e){
178 ypjax(e.currentTarget.href,"journal",function(){show_more_event();tooltip_activate();});
178 ypjax(e.currentTarget.href,"journal",function(){show_more_event();tooltip_activate();});
179 YUE.preventDefault(e);
179 YUE.preventDefault(e);
180 });
180 });
181
181
182
182
183 // main table sorting
183 // main table sorting
184 var myColumnDefs = [
184 var myColumnDefs = [
185 {key:"menu",label:"",sortable:false,className:"quick_repo_menu hidden"},
185 {key:"menu",label:"",sortable:false,className:"quick_repo_menu hidden"},
186 {key:"name",label:"${_('Name')}",sortable:true,
186 {key:"name",label:"${_('Name')}",sortable:true,
187 sortOptions: { sortFunction: nameSort }},
187 sortOptions: { sortFunction: nameSort }},
188 {key:"tip",label:"${_('Tip')}",sortable:true,
188 {key:"tip",label:"${_('Tip')}",sortable:true,
189 sortOptions: { sortFunction: revisionSort }},
189 sortOptions: { sortFunction: revisionSort }},
190 {key:"action1",label:"",sortable:false},
190 {key:"action1",label:"",sortable:false},
191 {key:"action2",label:"",sortable:false},
191 {key:"action2",label:"",sortable:false},
192 ];
192 ];
193
193
194 var myDataSource = new YAHOO.util.DataSource(YUD.get("repos_list"));
194 var myDataSource = new YAHOO.util.DataSource(YUD.get("repos_list"));
195
195
196 myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
196 myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
197
197
198 myDataSource.responseSchema = {
198 myDataSource.responseSchema = {
199 fields: [
199 fields: [
200 {key:"menu"},
200 {key:"menu"},
201 {key:"name"},
201 {key:"name"},
202 {key:"tip"},
202 {key:"tip"},
203 {key:"action1"},
203 {key:"action1"},
204 {key:"action2"}
204 {key:"action2"}
205 ]
205 ]
206 };
206 };
207
207
208 var myDataTable = new YAHOO.widget.DataTable("repos_list_wrap", myColumnDefs, myDataSource,
208 var myDataTable = new YAHOO.widget.DataTable("repos_list_wrap", myColumnDefs, myDataSource,
209 {
209 {
210 sortedBy:{key:"name",dir:"asc"},
210 sortedBy:{key:"name",dir:"asc"},
211 MSG_SORTASC:"${_('Click to sort ascending')}",
211 MSG_SORTASC:"${_('Click to sort ascending')}",
212 MSG_SORTDESC:"${_('Click to sort descending')}",
212 MSG_SORTDESC:"${_('Click to sort descending')}",
213 MSG_EMPTY:"${_('No records found.')}",
213 MSG_EMPTY:"${_('No records found.')}",
214 MSG_ERROR:"${_('Data error.')}",
214 MSG_ERROR:"${_('Data error.')}",
215 MSG_LOADING:"${_('Loading...')}",
215 MSG_LOADING:"${_('Loading...')}",
216 }
216 }
217 );
217 );
218 myDataTable.subscribe('postRenderEvent',function(oArgs) {
218 myDataTable.subscribe('postRenderEvent',function(oArgs) {
219 tooltip_activate();
219 tooltip_activate();
220 quick_repo_menu();
220 quick_repo_menu();
221 var func = function(node){
221 var func = function(node){
222 return node.parentNode.parentNode.parentNode.parentNode;
222 return node.parentNode.parentNode.parentNode.parentNode;
223 }
223 }
224 q_filter('q_filter',YUQ('#my tr td a.repo_name'),func);
224 q_filter('q_filter',YUQ('#my tr td a.repo_name'),func);
225 });
225 });
226
226
227
227
228 </script>
228 </script>
229 </%def>
229 </%def>
@@ -1,20 +1,20 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2
2
3 <li class="qfilter_rs">
3 <li class="qfilter_rs">
4 <input type="text" style="border:0;width:100%" value="${_('quick filter...')}" name="filter" id="q_filter_rs" />
4 <input type="text" style="border:0;width:100%" value="${_('quick filter...')}" name="filter" id="q_filter_rs" />
5 </li>
5 </li>
6
6
7 %for repo in c.repos_list:
7 %for repo in c.repos_list:
8
8
9 %if repo['dbrepo']['private']:
9 %if repo['dbrepo']['private'] and c.visual.show_private_icon:
10 <li>
10 <li>
11 <img src="${h.url('/images/icons/lock.png')}" alt="${_('Private repository')}" class="repo_switcher_type"/>
11 <img src="${h.url('/images/icons/lock.png')}" alt="${_('Private repository')}" class="repo_switcher_type"/>
12 ${h.link_to(repo['name'],h.url('summary_home',repo_name=repo['name']),class_="repo_name %s" % repo['dbrepo']['repo_type'])}
12 ${h.link_to(repo['name'],h.url('summary_home',repo_name=repo['name']),class_="repo_name %s" % repo['dbrepo']['repo_type'])}
13 </li>
13 </li>
14 %else:
14 %elif not repo['dbrepo']['private'] and c.visual.show_public_icon:
15 <li>
15 <li>
16 <img src="${h.url('/images/icons/lock_open.png')}" alt="${_('Public repository')}" class="repo_switcher_type" />
16 <img src="${h.url('/images/icons/lock_open.png')}" alt="${_('Public repository')}" class="repo_switcher_type" />
17 ${h.link_to(repo['name'],h.url('summary_home',repo_name=repo['name']),class_="repo_name %s" % repo['dbrepo']['repo_type'])}
17 ${h.link_to(repo['name'],h.url('summary_home',repo_name=repo['name']),class_="repo_name %s" % repo['dbrepo']['repo_type'])}
18 </li>
18 </li>
19 %endif
19 %endif
20 %endfor
20 %endfor
@@ -1,706 +1,710 b''
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${_('%s Summary') % c.repo_name} - ${c.rhodecode_name}
4 ${_('%s Summary') % c.repo_name} - ${c.rhodecode_name}
5 </%def>
5 </%def>
6
6
7 <%def name="breadcrumbs_links()">
7 <%def name="breadcrumbs_links()">
8 ${h.link_to(_(u'Home'),h.url('/'))}
8 ${h.link_to(_(u'Home'),h.url('/'))}
9 &raquo;
9 &raquo;
10 ${h.link_to(c.dbrepo.just_name,h.url('summary_home',repo_name=c.repo_name))}
10 ${h.link_to(c.dbrepo.just_name,h.url('summary_home',repo_name=c.repo_name))}
11 &raquo;
11 &raquo;
12 ${_('summary')}
12 ${_('summary')}
13 </%def>
13 </%def>
14
14
15 <%def name="page_nav()">
15 <%def name="page_nav()">
16 ${self.menu('summary')}
16 ${self.menu('summary')}
17 </%def>
17 </%def>
18
18
19 <%def name="head_extra()">
19 <%def name="head_extra()">
20 <link href="${h.url('atom_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key)}" rel="alternate" title="${_('repo %s ATOM feed') % c.repo_name}" type="application/atom+xml" />
20 <link href="${h.url('atom_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key)}" rel="alternate" title="${_('repo %s ATOM feed') % c.repo_name}" type="application/atom+xml" />
21 <link href="${h.url('rss_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key)}" rel="alternate" title="${_('repo %s RSS feed') % c.repo_name}" type="application/rss+xml" />
21 <link href="${h.url('rss_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key)}" rel="alternate" title="${_('repo %s RSS feed') % c.repo_name}" type="application/rss+xml" />
22 </%def>
22 </%def>
23
23
24 <%def name="main()">
24 <%def name="main()">
25 <%
25 <%
26 summary = lambda n:{False:'summary-short'}.get(n)
26 summary = lambda n:{False:'summary-short'}.get(n)
27 %>
27 %>
28 %if c.show_stats:
28 %if c.show_stats:
29 <div class="box box-left">
29 <div class="box box-left">
30 %else:
30 %else:
31 <div class="box">
31 <div class="box">
32 %endif
32 %endif
33 <!-- box / title -->
33 <!-- box / title -->
34 <div class="title">
34 <div class="title">
35 ${self.breadcrumbs()}
35 ${self.breadcrumbs()}
36 </div>
36 </div>
37 <!-- end box / title -->
37 <!-- end box / title -->
38 <div class="form">
38 <div class="form">
39 <div id="summary" class="fields">
39 <div id="summary" class="fields">
40
40
41 <div class="field">
41 <div class="field">
42 <div class="label-summary">
42 <div class="label-summary">
43 <label>${_('Name')}:</label>
43 <label>${_('Name')}:</label>
44 </div>
44 </div>
45 <div class="input ${summary(c.show_stats)}">
45 <div class="input ${summary(c.show_stats)}">
46 <div style="float:right;padding:5px 0px 0px 5px">
46 <div style="float:right;padding:5px 0px 0px 5px">
47 %if c.rhodecode_user.username != 'default':
47 %if c.rhodecode_user.username != 'default':
48 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key),class_='rss_icon')}
48 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key),class_='rss_icon')}
49 ${h.link_to(_('ATOM'),h.url('atom_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key),class_='atom_icon')}
49 ${h.link_to(_('ATOM'),h.url('atom_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key),class_='atom_icon')}
50 %else:
50 %else:
51 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.dbrepo.repo_name),class_='rss_icon')}
51 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.dbrepo.repo_name),class_='rss_icon')}
52 ${h.link_to(_('ATOM'),h.url('atom_feed_home',repo_name=c.dbrepo.repo_name),class_='atom_icon')}
52 ${h.link_to(_('ATOM'),h.url('atom_feed_home',repo_name=c.dbrepo.repo_name),class_='atom_icon')}
53 %endif
53 %endif
54 </div>
54 </div>
55 %if c.rhodecode_user.username != 'default':
55 %if c.rhodecode_user.username != 'default':
56 %if c.following:
56 %if c.following:
57 <span id="follow_toggle" class="following" title="${_('Stop following this repository')}"
57 <span id="follow_toggle" class="following" title="${_('Stop following this repository')}"
58 onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
58 onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
59 </span>
59 </span>
60 %else:
60 %else:
61 <span id="follow_toggle" class="follow" title="${_('Start following this repository')}"
61 <span id="follow_toggle" class="follow" title="${_('Start following this repository')}"
62 onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
62 onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
63 </span>
63 </span>
64 %endif
64 %endif
65 %endif:
65 %endif:
66 ##REPO TYPE
66 ##REPO TYPE
67 %if h.is_hg(c.dbrepo):
67 %if h.is_hg(c.dbrepo):
68 <img style="margin-bottom:2px" class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url('/images/icons/hgicon.png')}"/>
68 <img style="margin-bottom:2px" class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url('/images/icons/hgicon.png')}"/>
69 %endif
69 %endif
70 %if h.is_git(c.dbrepo):
70 %if h.is_git(c.dbrepo):
71 <img style="margin-bottom:2px" class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url('/images/icons/giticon.png')}"/>
71 <img style="margin-bottom:2px" class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url('/images/icons/giticon.png')}"/>
72 %endif
72 %endif
73
73
74 ##PUBLIC/PRIVATE
74 ##PUBLIC/PRIVATE
75 %if c.dbrepo.private:
75 %if c.dbrepo.private:
76 <img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="${h.url('/images/icons/lock.png')}"/>
76 <img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="${h.url('/images/icons/lock.png')}"/>
77 %else:
77 %else:
78 <img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="${h.url('/images/icons/lock_open.png')}"/>
78 <img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="${h.url('/images/icons/lock_open.png')}"/>
79 %endif
79 %endif
80
80
81 ##REPO NAME
81 ##REPO NAME
82 <span class="repo_name" title="${_('Non changable ID %s') % c.dbrepo.repo_id}">${h.repo_link(c.dbrepo.groups_and_repo)}</span>
82 <span class="repo_name" title="${_('Non changable ID %s') % c.dbrepo.repo_id}">${h.repo_link(c.dbrepo.groups_and_repo)}</span>
83
83
84 ##FORK
84 ##FORK
85 %if c.dbrepo.fork:
85 %if c.dbrepo.fork:
86 <div style="margin-top:5px;clear:both"">
86 <div style="margin-top:5px;clear:both"">
87 <a href="${h.url('summary_home',repo_name=c.dbrepo.fork.repo_name)}"><img class="icon" alt="${_('public')}" title="${_('Fork of')} ${c.dbrepo.fork.repo_name}" src="${h.url('/images/icons/arrow_divide.png')}"/>
87 <a href="${h.url('summary_home',repo_name=c.dbrepo.fork.repo_name)}"><img class="icon" alt="${_('public')}" title="${_('Fork of')} ${c.dbrepo.fork.repo_name}" src="${h.url('/images/icons/arrow_divide.png')}"/>
88 ${_('Fork of')} ${c.dbrepo.fork.repo_name}
88 ${_('Fork of')} ${c.dbrepo.fork.repo_name}
89 </a>
89 </a>
90 </div>
90 </div>
91 %endif
91 %endif
92 ##REMOTE
92 ##REMOTE
93 %if c.dbrepo.clone_uri:
93 %if c.dbrepo.clone_uri:
94 <div style="margin-top:5px;clear:both">
94 <div style="margin-top:5px;clear:both">
95 <a href="${h.url(str(h.hide_credentials(c.dbrepo.clone_uri)))}"><img class="icon" alt="${_('remote clone')}" title="${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}" src="${h.url('/images/icons/connect.png')}"/>
95 <a href="${h.url(str(h.hide_credentials(c.dbrepo.clone_uri)))}"><img class="icon" alt="${_('remote clone')}" title="${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}" src="${h.url('/images/icons/connect.png')}"/>
96 ${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}
96 ${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}
97 </a>
97 </a>
98 </div>
98 </div>
99 %endif
99 %endif
100 </div>
100 </div>
101 </div>
101 </div>
102
102
103 <div class="field">
103 <div class="field">
104 <div class="label-summary">
104 <div class="label-summary">
105 <label>${_('Description')}:</label>
105 <label>${_('Description')}:</label>
106 </div>
106 </div>
107 <div class="input ${summary(c.show_stats)} desc">${h.urlify_text(c.dbrepo.description)}</div>
107 %if c.visual.stylify_metatags:
108 <div class="input ${summary(c.show_stats)} desc">${h.urlify_text(h.desc_stylize(c.dbrepo.description))}</div>
109 %else:
110 <div class="input ${summary(c.show_stats)} desc">${h.urlify_text(c.dbrepo.description)}</div>
111 %endif
108 </div>
112 </div>
109
113
110 <div class="field">
114 <div class="field">
111 <div class="label-summary">
115 <div class="label-summary">
112 <label>${_('Contact')}:</label>
116 <label>${_('Contact')}:</label>
113 </div>
117 </div>
114 <div class="input ${summary(c.show_stats)}">
118 <div class="input ${summary(c.show_stats)}">
115 <div class="gravatar">
119 <div class="gravatar">
116 <img alt="gravatar" src="${h.gravatar_url(c.dbrepo.user.email)}"/>
120 <img alt="gravatar" src="${h.gravatar_url(c.dbrepo.user.email)}"/>
117 </div>
121 </div>
118 ${_('Username')}: ${c.dbrepo.user.username}<br/>
122 ${_('Username')}: ${c.dbrepo.user.username}<br/>
119 ${_('Name')}: ${c.dbrepo.user.name} ${c.dbrepo.user.lastname}<br/>
123 ${_('Name')}: ${c.dbrepo.user.name} ${c.dbrepo.user.lastname}<br/>
120 ${_('Email')}: <a href="mailto:${c.dbrepo.user.email}">${c.dbrepo.user.email}</a>
124 ${_('Email')}: <a href="mailto:${c.dbrepo.user.email}">${c.dbrepo.user.email}</a>
121 </div>
125 </div>
122 </div>
126 </div>
123
127
124 <div class="field">
128 <div class="field">
125 <div class="label-summary">
129 <div class="label-summary">
126 <label>${_('Clone url')}:</label>
130 <label>${_('Clone url')}:</label>
127 </div>
131 </div>
128 <div class="input ${summary(c.show_stats)}">
132 <div class="input ${summary(c.show_stats)}">
129 <div style="display:none" id="clone_by_name" class="ui-btn clone">${_('Show by Name')}</div>
133 <div style="display:none" id="clone_by_name" class="ui-btn clone">${_('Show by Name')}</div>
130 <div id="clone_by_id" class="ui-btn clone">${_('Show by ID')}</div>
134 <div id="clone_by_id" class="ui-btn clone">${_('Show by ID')}</div>
131 <input style="width:80%;margin-left:105px" type="text" id="clone_url" readonly="readonly" value="${c.clone_repo_url}"/>
135 <input style="width:80%;margin-left:105px" type="text" id="clone_url" readonly="readonly" value="${c.clone_repo_url}"/>
132 <input style="display:none;width:80%;margin-left:105px" type="text" id="clone_url_id" readonly="readonly" value="${c.clone_repo_url_id}"/>
136 <input style="display:none;width:80%;margin-left:105px" type="text" id="clone_url_id" readonly="readonly" value="${c.clone_repo_url_id}"/>
133 </div>
137 </div>
134 </div>
138 </div>
135
139
136 <div class="field">
140 <div class="field">
137 <div class="label-summary">
141 <div class="label-summary">
138 <label>${_('Trending files')}:</label>
142 <label>${_('Trending files')}:</label>
139 </div>
143 </div>
140 <div class="input ${summary(c.show_stats)}">
144 <div class="input ${summary(c.show_stats)}">
141 %if c.show_stats:
145 %if c.show_stats:
142 <div id="lang_stats"></div>
146 <div id="lang_stats"></div>
143 %else:
147 %else:
144 ${_('Statistics are disabled for this repository')}
148 ${_('Statistics are disabled for this repository')}
145 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
149 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
146 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-btn")}
150 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-btn")}
147 %endif
151 %endif
148 %endif
152 %endif
149 </div>
153 </div>
150 </div>
154 </div>
151
155
152 <div class="field">
156 <div class="field">
153 <div class="label-summary">
157 <div class="label-summary">
154 <label>${_('Download')}:</label>
158 <label>${_('Download')}:</label>
155 </div>
159 </div>
156 <div class="input ${summary(c.show_stats)}">
160 <div class="input ${summary(c.show_stats)}">
157 %if len(c.rhodecode_repo.revisions) == 0:
161 %if len(c.rhodecode_repo.revisions) == 0:
158 ${_('There are no downloads yet')}
162 ${_('There are no downloads yet')}
159 %elif c.enable_downloads is False:
163 %elif c.enable_downloads is False:
160 ${_('Downloads are disabled for this repository')}
164 ${_('Downloads are disabled for this repository')}
161 %if h.HasPermissionAll('hg.admin')('enable downloads on from summary'):
165 %if h.HasPermissionAll('hg.admin')('enable downloads on from summary'):
162 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-btn")}
166 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-btn")}
163 %endif
167 %endif
164 %else:
168 %else:
165 ${h.select('download_options',c.rhodecode_repo.get_changeset().raw_id,c.download_options)}
169 ${h.select('download_options',c.rhodecode_repo.get_changeset().raw_id,c.download_options)}
166 <span id="${'zip_link'}">${h.link_to(_('Download as zip'), h.url('files_archive_home',repo_name=c.dbrepo.repo_name,fname='tip.zip'),class_="archive_icon ui-btn")}</span>
170 <span id="${'zip_link'}">${h.link_to(_('Download as zip'), h.url('files_archive_home',repo_name=c.dbrepo.repo_name,fname='tip.zip'),class_="archive_icon ui-btn")}</span>
167 <span style="vertical-align: bottom">
171 <span style="vertical-align: bottom">
168 <input id="archive_subrepos" type="checkbox" name="subrepos" />
172 <input id="archive_subrepos" type="checkbox" name="subrepos" />
169 <label for="archive_subrepos" class="tooltip" title="${h.tooltip(_('Check this to download archive with subrepos'))}" >${_('with subrepos')}</label>
173 <label for="archive_subrepos" class="tooltip" title="${h.tooltip(_('Check this to download archive with subrepos'))}" >${_('with subrepos')}</label>
170 </span>
174 </span>
171 %endif
175 %endif
172 </div>
176 </div>
173 </div>
177 </div>
174 </div>
178 </div>
175 </div>
179 </div>
176 </div>
180 </div>
177
181
178 %if c.show_stats:
182 %if c.show_stats:
179 <div class="box box-right" style="min-height:455px">
183 <div class="box box-right" style="min-height:455px">
180 <!-- box / title -->
184 <!-- box / title -->
181 <div class="title">
185 <div class="title">
182 <h5>${_('Commit activity by day / author')}</h5>
186 <h5>${_('Commit activity by day / author')}</h5>
183 </div>
187 </div>
184
188
185 <div class="graph">
189 <div class="graph">
186 <div style="padding:0 10px 10px 17px;">
190 <div style="padding:0 10px 10px 17px;">
187 %if c.no_data:
191 %if c.no_data:
188 ${c.no_data_msg}
192 ${c.no_data_msg}
189 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
193 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
190 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-btn")}
194 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-btn")}
191 %endif
195 %endif
192 %else:
196 %else:
193 ${_('Stats gathered: ')} ${c.stats_percentage}%
197 ${_('Stats gathered: ')} ${c.stats_percentage}%
194 %endif
198 %endif
195 </div>
199 </div>
196 <div id="commit_history" style="width:450px;height:300px;float:left"></div>
200 <div id="commit_history" style="width:450px;height:300px;float:left"></div>
197 <div style="clear: both;height: 10px"></div>
201 <div style="clear: both;height: 10px"></div>
198 <div id="overview" style="width:450px;height:100px;float:left"></div>
202 <div id="overview" style="width:450px;height:100px;float:left"></div>
199
203
200 <div id="legend_data" style="clear:both;margin-top:10px;">
204 <div id="legend_data" style="clear:both;margin-top:10px;">
201 <div id="legend_container"></div>
205 <div id="legend_container"></div>
202 <div id="legend_choices">
206 <div id="legend_choices">
203 <table id="legend_choices_tables" class="noborder" style="font-size:smaller;color:#545454"></table>
207 <table id="legend_choices_tables" class="noborder" style="font-size:smaller;color:#545454"></table>
204 </div>
208 </div>
205 </div>
209 </div>
206 </div>
210 </div>
207 </div>
211 </div>
208 %endif
212 %endif
209
213
210 <div class="box">
214 <div class="box">
211 <div class="title">
215 <div class="title">
212 <div class="breadcrumbs">
216 <div class="breadcrumbs">
213 %if c.repo_changesets:
217 %if c.repo_changesets:
214 ${h.link_to(_('Shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}
218 ${h.link_to(_('Shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}
215 %else:
219 %else:
216 ${_('Quick start')}
220 ${_('Quick start')}
217 %endif
221 %endif
218 </div>
222 </div>
219 </div>
223 </div>
220 <div class="table">
224 <div class="table">
221 <div id="shortlog_data">
225 <div id="shortlog_data">
222 <%include file='../shortlog/shortlog_data.html'/>
226 <%include file='../shortlog/shortlog_data.html'/>
223 </div>
227 </div>
224 </div>
228 </div>
225 </div>
229 </div>
226
230
227 %if c.readme_data:
231 %if c.readme_data:
228 <div id="readme" class="box header-pos-fix" style="background-color: #FAFAFA">
232 <div id="readme" class="box header-pos-fix" style="background-color: #FAFAFA">
229 <div id="readme" class="title" title="${_("Readme file at revision '%s'" % c.rhodecode_db_repo.landing_rev)}">
233 <div id="readme" class="title" title="${_("Readme file at revision '%s'" % c.rhodecode_db_repo.landing_rev)}">
230 <div class="breadcrumbs">
234 <div class="breadcrumbs">
231 <a href="${h.url('files_home',repo_name=c.repo_name,revision='tip',f_path=c.readme_file)}">${c.readme_file}</a>
235 <a href="${h.url('files_home',repo_name=c.repo_name,revision='tip',f_path=c.readme_file)}">${c.readme_file}</a>
232 <a class="permalink" href="#readme" title="${_('Permalink to this readme')}">&para;</a>
236 <a class="permalink" href="#readme" title="${_('Permalink to this readme')}">&para;</a>
233 </div>
237 </div>
234 </div>
238 </div>
235 <div id="readme" class="readme">
239 <div id="readme" class="readme">
236 <div class="readme_box">
240 <div class="readme_box">
237 ${c.readme_data|n}
241 ${c.readme_data|n}
238 </div>
242 </div>
239 </div>
243 </div>
240 </div>
244 </div>
241 %endif
245 %endif
242
246
243 <script type="text/javascript">
247 <script type="text/javascript">
244 var clone_url = 'clone_url';
248 var clone_url = 'clone_url';
245 YUE.on(clone_url,'click',function(e){
249 YUE.on(clone_url,'click',function(e){
246 if(YUD.hasClass(clone_url,'selected')){
250 if(YUD.hasClass(clone_url,'selected')){
247 return
251 return
248 }
252 }
249 else{
253 else{
250 YUD.addClass(clone_url,'selected');
254 YUD.addClass(clone_url,'selected');
251 YUD.get(clone_url).select();
255 YUD.get(clone_url).select();
252 }
256 }
253 })
257 })
254
258
255 YUE.on('clone_by_name','click',function(e){
259 YUE.on('clone_by_name','click',function(e){
256 // show url by name and hide name button
260 // show url by name and hide name button
257 YUD.setStyle('clone_url','display','');
261 YUD.setStyle('clone_url','display','');
258 YUD.setStyle('clone_by_name','display','none');
262 YUD.setStyle('clone_by_name','display','none');
259
263
260 // hide url by id and show name button
264 // hide url by id and show name button
261 YUD.setStyle('clone_by_id','display','');
265 YUD.setStyle('clone_by_id','display','');
262 YUD.setStyle('clone_url_id','display','none');
266 YUD.setStyle('clone_url_id','display','none');
263
267
264 })
268 })
265 YUE.on('clone_by_id','click',function(e){
269 YUE.on('clone_by_id','click',function(e){
266
270
267 // show url by id and hide id button
271 // show url by id and hide id button
268 YUD.setStyle('clone_by_id','display','none');
272 YUD.setStyle('clone_by_id','display','none');
269 YUD.setStyle('clone_url_id','display','');
273 YUD.setStyle('clone_url_id','display','');
270
274
271 // hide url by name and show id button
275 // hide url by name and show id button
272 YUD.setStyle('clone_by_name','display','');
276 YUD.setStyle('clone_by_name','display','');
273 YUD.setStyle('clone_url','display','none');
277 YUD.setStyle('clone_url','display','none');
274 })
278 })
275
279
276
280
277 var tmpl_links = {};
281 var tmpl_links = {};
278 %for cnt,archive in enumerate(c.rhodecode_repo._get_archives()):
282 %for cnt,archive in enumerate(c.rhodecode_repo._get_archives()):
279 tmpl_links["${archive['type']}"] = '${h.link_to('__NAME__', h.url('files_archive_home',repo_name=c.dbrepo.repo_name, fname='__CS__'+archive['extension'],subrepos='__SUB__'),class_='archive_icon ui-btn')}';
283 tmpl_links["${archive['type']}"] = '${h.link_to('__NAME__', h.url('files_archive_home',repo_name=c.dbrepo.repo_name, fname='__CS__'+archive['extension'],subrepos='__SUB__'),class_='archive_icon ui-btn')}';
280 %endfor
284 %endfor
281
285
282 YUE.on(['download_options','archive_subrepos'],'change',function(e){
286 YUE.on(['download_options','archive_subrepos'],'change',function(e){
283 var sm = YUD.get('download_options');
287 var sm = YUD.get('download_options');
284 var new_cs = sm.options[sm.selectedIndex];
288 var new_cs = sm.options[sm.selectedIndex];
285
289
286 for(k in tmpl_links){
290 for(k in tmpl_links){
287 var s = YUD.get(k+'_link');
291 var s = YUD.get(k+'_link');
288 if(s){
292 if(s){
289 var title_tmpl = "${_('Download %s as %s') % ('__CS_NAME__','__CS_EXT__')}";
293 var title_tmpl = "${_('Download %s as %s') % ('__CS_NAME__','__CS_EXT__')}";
290 title_tmpl= title_tmpl.replace('__CS_NAME__',new_cs.text);
294 title_tmpl= title_tmpl.replace('__CS_NAME__',new_cs.text);
291 title_tmpl = title_tmpl.replace('__CS_EXT__',k);
295 title_tmpl = title_tmpl.replace('__CS_EXT__',k);
292
296
293 var url = tmpl_links[k].replace('__CS__',new_cs.value);
297 var url = tmpl_links[k].replace('__CS__',new_cs.value);
294 var subrepos = YUD.get('archive_subrepos').checked;
298 var subrepos = YUD.get('archive_subrepos').checked;
295 url = url.replace('__SUB__',subrepos);
299 url = url.replace('__SUB__',subrepos);
296 url = url.replace('__NAME__',title_tmpl);
300 url = url.replace('__NAME__',title_tmpl);
297 s.innerHTML = url
301 s.innerHTML = url
298 }
302 }
299 }
303 }
300 });
304 });
301 </script>
305 </script>
302 %if c.show_stats:
306 %if c.show_stats:
303 <script type="text/javascript">
307 <script type="text/javascript">
304 var data = ${c.trending_languages|n};
308 var data = ${c.trending_languages|n};
305 var total = 0;
309 var total = 0;
306 var no_data = true;
310 var no_data = true;
307 var tbl = document.createElement('table');
311 var tbl = document.createElement('table');
308 tbl.setAttribute('class','trending_language_tbl');
312 tbl.setAttribute('class','trending_language_tbl');
309 var cnt = 0;
313 var cnt = 0;
310 for (var i=0;i<data.length;i++){
314 for (var i=0;i<data.length;i++){
311 total+= data[i][1].count;
315 total+= data[i][1].count;
312 }
316 }
313 for (var i=0;i<data.length;i++){
317 for (var i=0;i<data.length;i++){
314 cnt += 1;
318 cnt += 1;
315 no_data = false;
319 no_data = false;
316
320
317 var hide = cnt>2;
321 var hide = cnt>2;
318 var tr = document.createElement('tr');
322 var tr = document.createElement('tr');
319 if (hide){
323 if (hide){
320 tr.setAttribute('style','display:none');
324 tr.setAttribute('style','display:none');
321 tr.setAttribute('class','stats_hidden');
325 tr.setAttribute('class','stats_hidden');
322 }
326 }
323 var k = data[i][0];
327 var k = data[i][0];
324 var obj = data[i][1];
328 var obj = data[i][1];
325 var percentage = Math.round((obj.count/total*100),2);
329 var percentage = Math.round((obj.count/total*100),2);
326
330
327 var td1 = document.createElement('td');
331 var td1 = document.createElement('td');
328 td1.width = 150;
332 td1.width = 150;
329 var trending_language_label = document.createElement('div');
333 var trending_language_label = document.createElement('div');
330 trending_language_label.innerHTML = obj.desc+" ("+k+")";
334 trending_language_label.innerHTML = obj.desc+" ("+k+")";
331 td1.appendChild(trending_language_label);
335 td1.appendChild(trending_language_label);
332
336
333 var td2 = document.createElement('td');
337 var td2 = document.createElement('td');
334 td2.setAttribute('style','padding-right:14px !important');
338 td2.setAttribute('style','padding-right:14px !important');
335 var trending_language = document.createElement('div');
339 var trending_language = document.createElement('div');
336 var nr_files = obj.count+" ${_('files')}";
340 var nr_files = obj.count+" ${_('files')}";
337
341
338 trending_language.title = k+" "+nr_files;
342 trending_language.title = k+" "+nr_files;
339
343
340 if (percentage>22){
344 if (percentage>22){
341 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"% "+nr_files+ "</b>";
345 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"% "+nr_files+ "</b>";
342 }
346 }
343 else{
347 else{
344 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"%</b>";
348 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"%</b>";
345 }
349 }
346
350
347 trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
351 trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
348 trending_language.style.width=percentage+"%";
352 trending_language.style.width=percentage+"%";
349 td2.appendChild(trending_language);
353 td2.appendChild(trending_language);
350
354
351 tr.appendChild(td1);
355 tr.appendChild(td1);
352 tr.appendChild(td2);
356 tr.appendChild(td2);
353 tbl.appendChild(tr);
357 tbl.appendChild(tr);
354 if(cnt == 3){
358 if(cnt == 3){
355 var show_more = document.createElement('tr');
359 var show_more = document.createElement('tr');
356 var td = document.createElement('td');
360 var td = document.createElement('td');
357 lnk = document.createElement('a');
361 lnk = document.createElement('a');
358
362
359 lnk.href='#';
363 lnk.href='#';
360 lnk.innerHTML = "${_('show more')}";
364 lnk.innerHTML = "${_('show more')}";
361 lnk.id='code_stats_show_more';
365 lnk.id='code_stats_show_more';
362 td.appendChild(lnk);
366 td.appendChild(lnk);
363
367
364 show_more.appendChild(td);
368 show_more.appendChild(td);
365 show_more.appendChild(document.createElement('td'));
369 show_more.appendChild(document.createElement('td'));
366 tbl.appendChild(show_more);
370 tbl.appendChild(show_more);
367 }
371 }
368
372
369 }
373 }
370
374
371 YUD.get('lang_stats').appendChild(tbl);
375 YUD.get('lang_stats').appendChild(tbl);
372 YUE.on('code_stats_show_more','click',function(){
376 YUE.on('code_stats_show_more','click',function(){
373 l = YUD.getElementsByClassName('stats_hidden')
377 l = YUD.getElementsByClassName('stats_hidden')
374 for (e in l){
378 for (e in l){
375 YUD.setStyle(l[e],'display','');
379 YUD.setStyle(l[e],'display','');
376 };
380 };
377 YUD.setStyle(YUD.get('code_stats_show_more'),
381 YUD.setStyle(YUD.get('code_stats_show_more'),
378 'display','none');
382 'display','none');
379 });
383 });
380 </script>
384 </script>
381 <script type="text/javascript">
385 <script type="text/javascript">
382 /**
386 /**
383 * Plots summary graph
387 * Plots summary graph
384 *
388 *
385 * @class SummaryPlot
389 * @class SummaryPlot
386 * @param {from} initial from for detailed graph
390 * @param {from} initial from for detailed graph
387 * @param {to} initial to for detailed graph
391 * @param {to} initial to for detailed graph
388 * @param {dataset}
392 * @param {dataset}
389 * @param {overview_dataset}
393 * @param {overview_dataset}
390 */
394 */
391 function SummaryPlot(from,to,dataset,overview_dataset) {
395 function SummaryPlot(from,to,dataset,overview_dataset) {
392 var initial_ranges = {
396 var initial_ranges = {
393 "xaxis":{
397 "xaxis":{
394 "from":from,
398 "from":from,
395 "to":to,
399 "to":to,
396 },
400 },
397 };
401 };
398 var dataset = dataset;
402 var dataset = dataset;
399 var overview_dataset = [overview_dataset];
403 var overview_dataset = [overview_dataset];
400 var choiceContainer = YUD.get("legend_choices");
404 var choiceContainer = YUD.get("legend_choices");
401 var choiceContainerTable = YUD.get("legend_choices_tables");
405 var choiceContainerTable = YUD.get("legend_choices_tables");
402 var plotContainer = YUD.get('commit_history');
406 var plotContainer = YUD.get('commit_history');
403 var overviewContainer = YUD.get('overview');
407 var overviewContainer = YUD.get('overview');
404
408
405 var plot_options = {
409 var plot_options = {
406 bars: {show:true,align:'center',lineWidth:4},
410 bars: {show:true,align:'center',lineWidth:4},
407 legend: {show:true, container:"legend_container"},
411 legend: {show:true, container:"legend_container"},
408 points: {show:true,radius:0,fill:false},
412 points: {show:true,radius:0,fill:false},
409 yaxis: {tickDecimals:0,},
413 yaxis: {tickDecimals:0,},
410 xaxis: {
414 xaxis: {
411 mode: "time",
415 mode: "time",
412 timeformat: "%d/%m",
416 timeformat: "%d/%m",
413 min:from,
417 min:from,
414 max:to,
418 max:to,
415 },
419 },
416 grid: {
420 grid: {
417 hoverable: true,
421 hoverable: true,
418 clickable: true,
422 clickable: true,
419 autoHighlight:true,
423 autoHighlight:true,
420 color: "#999"
424 color: "#999"
421 },
425 },
422 //selection: {mode: "x"}
426 //selection: {mode: "x"}
423 };
427 };
424 var overview_options = {
428 var overview_options = {
425 legend:{show:false},
429 legend:{show:false},
426 bars: {show:true,barWidth: 2,},
430 bars: {show:true,barWidth: 2,},
427 shadowSize: 0,
431 shadowSize: 0,
428 xaxis: {mode: "time", timeformat: "%d/%m/%y",},
432 xaxis: {mode: "time", timeformat: "%d/%m/%y",},
429 yaxis: {ticks: 3, min: 0,tickDecimals:0,},
433 yaxis: {ticks: 3, min: 0,tickDecimals:0,},
430 grid: {color: "#999",},
434 grid: {color: "#999",},
431 selection: {mode: "x"}
435 selection: {mode: "x"}
432 };
436 };
433
437
434 /**
438 /**
435 *get dummy data needed in few places
439 *get dummy data needed in few places
436 */
440 */
437 function getDummyData(label){
441 function getDummyData(label){
438 return {"label":label,
442 return {"label":label,
439 "data":[{"time":0,
443 "data":[{"time":0,
440 "commits":0,
444 "commits":0,
441 "added":0,
445 "added":0,
442 "changed":0,
446 "changed":0,
443 "removed":0,
447 "removed":0,
444 }],
448 }],
445 "schema":["commits"],
449 "schema":["commits"],
446 "color":'#ffffff',
450 "color":'#ffffff',
447 }
451 }
448 }
452 }
449
453
450 /**
454 /**
451 * generate checkboxes accordindly to data
455 * generate checkboxes accordindly to data
452 * @param keys
456 * @param keys
453 * @returns
457 * @returns
454 */
458 */
455 function generateCheckboxes(data) {
459 function generateCheckboxes(data) {
456 //append checkboxes
460 //append checkboxes
457 var i = 0;
461 var i = 0;
458 choiceContainerTable.innerHTML = '';
462 choiceContainerTable.innerHTML = '';
459 for(var pos in data) {
463 for(var pos in data) {
460
464
461 data[pos].color = i;
465 data[pos].color = i;
462 i++;
466 i++;
463 if(data[pos].label != ''){
467 if(data[pos].label != ''){
464 choiceContainerTable.innerHTML +=
468 choiceContainerTable.innerHTML +=
465 '<tr><td><input type="checkbox" id="id_user_{0}" name="{0}" checked="checked" /> \
469 '<tr><td><input type="checkbox" id="id_user_{0}" name="{0}" checked="checked" /> \
466 <label for="id_user_{0}">{0}</label></td></tr>'.format(data[pos].label);
470 <label for="id_user_{0}">{0}</label></td></tr>'.format(data[pos].label);
467 }
471 }
468 }
472 }
469 }
473 }
470
474
471 /**
475 /**
472 * ToolTip show
476 * ToolTip show
473 */
477 */
474 function showTooltip(x, y, contents) {
478 function showTooltip(x, y, contents) {
475 var div=document.getElementById('tooltip');
479 var div=document.getElementById('tooltip');
476 if(!div) {
480 if(!div) {
477 div = document.createElement('div');
481 div = document.createElement('div');
478 div.id="tooltip";
482 div.id="tooltip";
479 div.style.position="absolute";
483 div.style.position="absolute";
480 div.style.border='1px solid #fdd';
484 div.style.border='1px solid #fdd';
481 div.style.padding='2px';
485 div.style.padding='2px';
482 div.style.backgroundColor='#fee';
486 div.style.backgroundColor='#fee';
483 document.body.appendChild(div);
487 document.body.appendChild(div);
484 }
488 }
485 YUD.setStyle(div, 'opacity', 0);
489 YUD.setStyle(div, 'opacity', 0);
486 div.innerHTML = contents;
490 div.innerHTML = contents;
487 div.style.top=(y + 5) + "px";
491 div.style.top=(y + 5) + "px";
488 div.style.left=(x + 5) + "px";
492 div.style.left=(x + 5) + "px";
489
493
490 var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
494 var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
491 anim.animate();
495 anim.animate();
492 }
496 }
493
497
494 /**
498 /**
495 * This function will detect if selected period has some changesets
499 * This function will detect if selected period has some changesets
496 for this user if it does this data is then pushed for displaying
500 for this user if it does this data is then pushed for displaying
497 Additionally it will only display users that are selected by the checkbox
501 Additionally it will only display users that are selected by the checkbox
498 */
502 */
499 function getDataAccordingToRanges(ranges) {
503 function getDataAccordingToRanges(ranges) {
500
504
501 var data = [];
505 var data = [];
502 var new_dataset = {};
506 var new_dataset = {};
503 var keys = [];
507 var keys = [];
504 var max_commits = 0;
508 var max_commits = 0;
505 for(var key in dataset){
509 for(var key in dataset){
506
510
507 for(var ds in dataset[key].data){
511 for(var ds in dataset[key].data){
508 commit_data = dataset[key].data[ds];
512 commit_data = dataset[key].data[ds];
509 if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
513 if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
510
514
511 if(new_dataset[key] === undefined){
515 if(new_dataset[key] === undefined){
512 new_dataset[key] = {data:[],schema:["commits"],label:key};
516 new_dataset[key] = {data:[],schema:["commits"],label:key};
513 }
517 }
514 new_dataset[key].data.push(commit_data);
518 new_dataset[key].data.push(commit_data);
515 }
519 }
516 }
520 }
517 if (new_dataset[key] !== undefined){
521 if (new_dataset[key] !== undefined){
518 data.push(new_dataset[key]);
522 data.push(new_dataset[key]);
519 }
523 }
520 }
524 }
521
525
522 if (data.length > 0){
526 if (data.length > 0){
523 return data;
527 return data;
524 }
528 }
525 else{
529 else{
526 //just return dummy data for graph to plot itself
530 //just return dummy data for graph to plot itself
527 return [getDummyData('')];
531 return [getDummyData('')];
528 }
532 }
529 }
533 }
530
534
531 /**
535 /**
532 * redraw using new checkbox data
536 * redraw using new checkbox data
533 */
537 */
534 function plotchoiced(e,args){
538 function plotchoiced(e,args){
535 var cur_data = args[0];
539 var cur_data = args[0];
536 var cur_ranges = args[1];
540 var cur_ranges = args[1];
537
541
538 var new_data = [];
542 var new_data = [];
539 var inputs = choiceContainer.getElementsByTagName("input");
543 var inputs = choiceContainer.getElementsByTagName("input");
540
544
541 //show only checked labels
545 //show only checked labels
542 for(var i=0; i<inputs.length; i++) {
546 for(var i=0; i<inputs.length; i++) {
543 var checkbox_key = inputs[i].name;
547 var checkbox_key = inputs[i].name;
544
548
545 if(inputs[i].checked){
549 if(inputs[i].checked){
546 for(var d in cur_data){
550 for(var d in cur_data){
547 if(cur_data[d].label == checkbox_key){
551 if(cur_data[d].label == checkbox_key){
548 new_data.push(cur_data[d]);
552 new_data.push(cur_data[d]);
549 }
553 }
550 }
554 }
551 }
555 }
552 else{
556 else{
553 //push dummy data to not hide the label
557 //push dummy data to not hide the label
554 new_data.push(getDummyData(checkbox_key));
558 new_data.push(getDummyData(checkbox_key));
555 }
559 }
556 }
560 }
557
561
558 var new_options = YAHOO.lang.merge(plot_options, {
562 var new_options = YAHOO.lang.merge(plot_options, {
559 xaxis: {
563 xaxis: {
560 min: cur_ranges.xaxis.from,
564 min: cur_ranges.xaxis.from,
561 max: cur_ranges.xaxis.to,
565 max: cur_ranges.xaxis.to,
562 mode:"time",
566 mode:"time",
563 timeformat: "%d/%m",
567 timeformat: "%d/%m",
564 },
568 },
565 });
569 });
566 if (!new_data){
570 if (!new_data){
567 new_data = [[0,1]];
571 new_data = [[0,1]];
568 }
572 }
569 // do the zooming
573 // do the zooming
570 plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
574 plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
571
575
572 plot.subscribe("plotselected", plotselected);
576 plot.subscribe("plotselected", plotselected);
573
577
574 //resubscribe plothover
578 //resubscribe plothover
575 plot.subscribe("plothover", plothover);
579 plot.subscribe("plothover", plothover);
576
580
577 // don't fire event on the overview to prevent eternal loop
581 // don't fire event on the overview to prevent eternal loop
578 overview.setSelection(cur_ranges, true);
582 overview.setSelection(cur_ranges, true);
579
583
580 }
584 }
581
585
582 /**
586 /**
583 * plot only selected items from overview
587 * plot only selected items from overview
584 * @param ranges
588 * @param ranges
585 * @returns
589 * @returns
586 */
590 */
587 function plotselected(ranges,cur_data) {
591 function plotselected(ranges,cur_data) {
588 //updates the data for new plot
592 //updates the data for new plot
589 var data = getDataAccordingToRanges(ranges);
593 var data = getDataAccordingToRanges(ranges);
590 generateCheckboxes(data);
594 generateCheckboxes(data);
591
595
592 var new_options = YAHOO.lang.merge(plot_options, {
596 var new_options = YAHOO.lang.merge(plot_options, {
593 xaxis: {
597 xaxis: {
594 min: ranges.xaxis.from,
598 min: ranges.xaxis.from,
595 max: ranges.xaxis.to,
599 max: ranges.xaxis.to,
596 mode:"time",
600 mode:"time",
597 timeformat: "%d/%m",
601 timeformat: "%d/%m",
598 },
602 },
599 });
603 });
600 // do the zooming
604 // do the zooming
601 plot = YAHOO.widget.Flot(plotContainer, data, new_options);
605 plot = YAHOO.widget.Flot(plotContainer, data, new_options);
602
606
603 plot.subscribe("plotselected", plotselected);
607 plot.subscribe("plotselected", plotselected);
604
608
605 //resubscribe plothover
609 //resubscribe plothover
606 plot.subscribe("plothover", plothover);
610 plot.subscribe("plothover", plothover);
607
611
608 // don't fire event on the overview to prevent eternal loop
612 // don't fire event on the overview to prevent eternal loop
609 overview.setSelection(ranges, true);
613 overview.setSelection(ranges, true);
610
614
611 //resubscribe choiced
615 //resubscribe choiced
612 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
616 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
613 }
617 }
614
618
615 var previousPoint = null;
619 var previousPoint = null;
616
620
617 function plothover(o) {
621 function plothover(o) {
618 var pos = o.pos;
622 var pos = o.pos;
619 var item = o.item;
623 var item = o.item;
620
624
621 //YUD.get("x").innerHTML = pos.x.toFixed(2);
625 //YUD.get("x").innerHTML = pos.x.toFixed(2);
622 //YUD.get("y").innerHTML = pos.y.toFixed(2);
626 //YUD.get("y").innerHTML = pos.y.toFixed(2);
623 if (item) {
627 if (item) {
624 if (previousPoint != item.datapoint) {
628 if (previousPoint != item.datapoint) {
625 previousPoint = item.datapoint;
629 previousPoint = item.datapoint;
626
630
627 var tooltip = YUD.get("tooltip");
631 var tooltip = YUD.get("tooltip");
628 if(tooltip) {
632 if(tooltip) {
629 tooltip.parentNode.removeChild(tooltip);
633 tooltip.parentNode.removeChild(tooltip);
630 }
634 }
631 var x = item.datapoint.x.toFixed(2);
635 var x = item.datapoint.x.toFixed(2);
632 var y = item.datapoint.y.toFixed(2);
636 var y = item.datapoint.y.toFixed(2);
633
637
634 if (!item.series.label){
638 if (!item.series.label){
635 item.series.label = 'commits';
639 item.series.label = 'commits';
636 }
640 }
637 var d = new Date(x*1000);
641 var d = new Date(x*1000);
638 var fd = d.toDateString()
642 var fd = d.toDateString()
639 var nr_commits = parseInt(y);
643 var nr_commits = parseInt(y);
640
644
641 var cur_data = dataset[item.series.label].data[item.dataIndex];
645 var cur_data = dataset[item.series.label].data[item.dataIndex];
642 var added = cur_data.added;
646 var added = cur_data.added;
643 var changed = cur_data.changed;
647 var changed = cur_data.changed;
644 var removed = cur_data.removed;
648 var removed = cur_data.removed;
645
649
646 var nr_commits_suffix = " ${_('commits')} ";
650 var nr_commits_suffix = " ${_('commits')} ";
647 var added_suffix = " ${_('files added')} ";
651 var added_suffix = " ${_('files added')} ";
648 var changed_suffix = " ${_('files changed')} ";
652 var changed_suffix = " ${_('files changed')} ";
649 var removed_suffix = " ${_('files removed')} ";
653 var removed_suffix = " ${_('files removed')} ";
650
654
651
655
652 if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
656 if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
653 if(added==1){added_suffix=" ${_('file added')} ";}
657 if(added==1){added_suffix=" ${_('file added')} ";}
654 if(changed==1){changed_suffix=" ${_('file changed')} ";}
658 if(changed==1){changed_suffix=" ${_('file changed')} ";}
655 if(removed==1){removed_suffix=" ${_('file removed')} ";}
659 if(removed==1){removed_suffix=" ${_('file removed')} ";}
656
660
657 showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
661 showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
658 +'<br/>'+
662 +'<br/>'+
659 nr_commits + nr_commits_suffix+'<br/>'+
663 nr_commits + nr_commits_suffix+'<br/>'+
660 added + added_suffix +'<br/>'+
664 added + added_suffix +'<br/>'+
661 changed + changed_suffix + '<br/>'+
665 changed + changed_suffix + '<br/>'+
662 removed + removed_suffix + '<br/>');
666 removed + removed_suffix + '<br/>');
663 }
667 }
664 }
668 }
665 else {
669 else {
666 var tooltip = YUD.get("tooltip");
670 var tooltip = YUD.get("tooltip");
667
671
668 if(tooltip) {
672 if(tooltip) {
669 tooltip.parentNode.removeChild(tooltip);
673 tooltip.parentNode.removeChild(tooltip);
670 }
674 }
671 previousPoint = null;
675 previousPoint = null;
672 }
676 }
673 }
677 }
674
678
675 /**
679 /**
676 * MAIN EXECUTION
680 * MAIN EXECUTION
677 */
681 */
678
682
679 var data = getDataAccordingToRanges(initial_ranges);
683 var data = getDataAccordingToRanges(initial_ranges);
680 generateCheckboxes(data);
684 generateCheckboxes(data);
681
685
682 //main plot
686 //main plot
683 var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
687 var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
684
688
685 //overview
689 //overview
686 var overview = YAHOO.widget.Flot(overviewContainer,
690 var overview = YAHOO.widget.Flot(overviewContainer,
687 overview_dataset, overview_options);
691 overview_dataset, overview_options);
688
692
689 //show initial selection on overview
693 //show initial selection on overview
690 overview.setSelection(initial_ranges);
694 overview.setSelection(initial_ranges);
691
695
692 plot.subscribe("plotselected", plotselected);
696 plot.subscribe("plotselected", plotselected);
693 plot.subscribe("plothover", plothover)
697 plot.subscribe("plothover", plothover)
694
698
695 overview.subscribe("plotselected", function (ranges) {
699 overview.subscribe("plotselected", function (ranges) {
696 plot.setSelection(ranges);
700 plot.setSelection(ranges);
697 });
701 });
698
702
699 // user choices on overview
703 // user choices on overview
700 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
704 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
701 }
705 }
702 SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});
706 SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});
703 </script>
707 </script>
704 %endif
708 %endif
705
709
706 </%def>
710 </%def>
@@ -1,133 +1,149 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.tests.test_libs
3 rhodecode.tests.test_libs
4 ~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6
6
7 Package for testing various lib/helper functions in rhodecode
7 Package for testing various lib/helper functions in rhodecode
8
8
9 :created_on: Jun 9, 2011
9 :created_on: Jun 9, 2011
10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2011-2012 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 unittest
26 import unittest
27 import datetime
27 import datetime
28 from rhodecode.tests import *
28 from rhodecode.tests import *
29
29
30
30
31 proto = 'http'
31 proto = 'http'
32 TEST_URLS = [
32 TEST_URLS = [
33 ('%s://127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
33 ('%s://127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
34 '%s://127.0.0.1' % proto),
34 '%s://127.0.0.1' % proto),
35 ('%s://marcink@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
35 ('%s://marcink@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
36 '%s://127.0.0.1' % proto),
36 '%s://127.0.0.1' % proto),
37 ('%s://marcink:pass@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
37 ('%s://marcink:pass@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
38 '%s://127.0.0.1' % proto),
38 '%s://127.0.0.1' % proto),
39 ('%s://127.0.0.1:8080' % proto, ['%s://' % proto, '127.0.0.1', '8080'],
39 ('%s://127.0.0.1:8080' % proto, ['%s://' % proto, '127.0.0.1', '8080'],
40 '%s://127.0.0.1:8080' % proto),
40 '%s://127.0.0.1:8080' % proto),
41 ('%s://domain.org' % proto, ['%s://' % proto, 'domain.org'],
41 ('%s://domain.org' % proto, ['%s://' % proto, 'domain.org'],
42 '%s://domain.org' % proto),
42 '%s://domain.org' % proto),
43 ('%s://user:pass@domain.org:8080' % proto, ['%s://' % proto, 'domain.org',
43 ('%s://user:pass@domain.org:8080' % proto, ['%s://' % proto, 'domain.org',
44 '8080'],
44 '8080'],
45 '%s://domain.org:8080' % proto),
45 '%s://domain.org:8080' % proto),
46 ]
46 ]
47
47
48 proto = 'https'
48 proto = 'https'
49 TEST_URLS += [
49 TEST_URLS += [
50 ('%s://127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
50 ('%s://127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
51 '%s://127.0.0.1' % proto),
51 '%s://127.0.0.1' % proto),
52 ('%s://marcink@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
52 ('%s://marcink@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
53 '%s://127.0.0.1' % proto),
53 '%s://127.0.0.1' % proto),
54 ('%s://marcink:pass@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
54 ('%s://marcink:pass@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
55 '%s://127.0.0.1' % proto),
55 '%s://127.0.0.1' % proto),
56 ('%s://127.0.0.1:8080' % proto, ['%s://' % proto, '127.0.0.1', '8080'],
56 ('%s://127.0.0.1:8080' % proto, ['%s://' % proto, '127.0.0.1', '8080'],
57 '%s://127.0.0.1:8080' % proto),
57 '%s://127.0.0.1:8080' % proto),
58 ('%s://domain.org' % proto, ['%s://' % proto, 'domain.org'],
58 ('%s://domain.org' % proto, ['%s://' % proto, 'domain.org'],
59 '%s://domain.org' % proto),
59 '%s://domain.org' % proto),
60 ('%s://user:pass@domain.org:8080' % proto, ['%s://' % proto, 'domain.org',
60 ('%s://user:pass@domain.org:8080' % proto, ['%s://' % proto, 'domain.org',
61 '8080'],
61 '8080'],
62 '%s://domain.org:8080' % proto),
62 '%s://domain.org:8080' % proto),
63 ]
63 ]
64
64
65
65
66 class TestLibs(unittest.TestCase):
66 class TestLibs(unittest.TestCase):
67
67
68 def test_uri_filter(self):
68 def test_uri_filter(self):
69 from rhodecode.lib.utils2 import uri_filter
69 from rhodecode.lib.utils2 import uri_filter
70
70
71 for url in TEST_URLS:
71 for url in TEST_URLS:
72 self.assertEqual(uri_filter(url[0]), url[1])
72 self.assertEqual(uri_filter(url[0]), url[1])
73
73
74 def test_credentials_filter(self):
74 def test_credentials_filter(self):
75 from rhodecode.lib.utils2 import credentials_filter
75 from rhodecode.lib.utils2 import credentials_filter
76
76
77 for url in TEST_URLS:
77 for url in TEST_URLS:
78 self.assertEqual(credentials_filter(url[0]), url[2])
78 self.assertEqual(credentials_filter(url[0]), url[2])
79
79
80 def test_str2bool(self):
80 def test_str2bool(self):
81 from rhodecode.lib.utils2 import str2bool
81 from rhodecode.lib.utils2 import str2bool
82 test_cases = [
82 test_cases = [
83 ('t', True),
83 ('t', True),
84 ('true', True),
84 ('true', True),
85 ('y', True),
85 ('y', True),
86 ('yes', True),
86 ('yes', True),
87 ('on', True),
87 ('on', True),
88 ('1', True),
88 ('1', True),
89 ('Y', True),
89 ('Y', True),
90 ('yeS', True),
90 ('yeS', True),
91 ('Y', True),
91 ('Y', True),
92 ('TRUE', True),
92 ('TRUE', True),
93 ('T', True),
93 ('T', True),
94 ('False', False),
94 ('False', False),
95 ('F', False),
95 ('F', False),
96 ('FALSE', False),
96 ('FALSE', False),
97 ('0', False),
97 ('0', False),
98 ('-1', False),
98 ('-1', False),
99 ('', False), ]
99 ('', False), ]
100
100
101 for case in test_cases:
101 for case in test_cases:
102 self.assertEqual(str2bool(case[0]), case[1])
102 self.assertEqual(str2bool(case[0]), case[1])
103
103
104 def test_mention_extractor(self):
104 def test_mention_extractor(self):
105 from rhodecode.lib.utils2 import extract_mentioned_users
105 from rhodecode.lib.utils2 import extract_mentioned_users
106 sample = (
106 sample = (
107 "@first hi there @marcink here's my email marcin@email.com "
107 "@first hi there @marcink here's my email marcin@email.com "
108 "@lukaszb check @one_more22 it pls @ ttwelve @D[] @one@two@three "
108 "@lukaszb check @one_more22 it pls @ ttwelve @D[] @one@two@three "
109 "@MARCIN @maRCiN @2one_more22 @john please see this http://org.pl "
109 "@MARCIN @maRCiN @2one_more22 @john please see this http://org.pl "
110 "@marian.user just do it @marco-polo and next extract @marco_polo "
110 "@marian.user just do it @marco-polo and next extract @marco_polo "
111 "user.dot hej ! not-needed maril@domain.org"
111 "user.dot hej ! not-needed maril@domain.org"
112 )
112 )
113
113
114 s = sorted([
114 s = sorted([
115 'first', 'marcink', 'lukaszb', 'one_more22', 'MARCIN', 'maRCiN', 'john',
115 'first', 'marcink', 'lukaszb', 'one_more22', 'MARCIN', 'maRCiN', 'john',
116 'marian.user', 'marco-polo', 'marco_polo'
116 'marian.user', 'marco-polo', 'marco_polo'
117 ], key=lambda k: k.lower())
117 ], key=lambda k: k.lower())
118 self.assertEqual(s, extract_mentioned_users(sample))
118 self.assertEqual(s, extract_mentioned_users(sample))
119
119
120 def test_age(self):
120 def test_age(self):
121 import calendar
121 import calendar
122 from rhodecode.lib.utils2 import age
122 from rhodecode.lib.utils2 import age
123 n = datetime.datetime.now()
123 n = datetime.datetime.now()
124 delt = lambda *args, **kwargs: datetime.timedelta(*args, **kwargs)
124 delt = lambda *args, **kwargs: datetime.timedelta(*args, **kwargs)
125 self.assertEqual(age(n), u'just now')
125 self.assertEqual(age(n), u'just now')
126 self.assertEqual(age(n - delt(seconds=1)), u'1 second ago')
126 self.assertEqual(age(n - delt(seconds=1)), u'1 second ago')
127 self.assertEqual(age(n - delt(seconds=60 * 2)), u'2 minutes ago')
127 self.assertEqual(age(n - delt(seconds=60 * 2)), u'2 minutes ago')
128 self.assertEqual(age(n - delt(hours=1)), u'1 hour ago')
128 self.assertEqual(age(n - delt(hours=1)), u'1 hour ago')
129 self.assertEqual(age(n - delt(hours=24)), u'1 day ago')
129 self.assertEqual(age(n - delt(hours=24)), u'1 day ago')
130 self.assertEqual(age(n - delt(hours=24 * 5)), u'5 days ago')
130 self.assertEqual(age(n - delt(hours=24 * 5)), u'5 days ago')
131 self.assertEqual(age(n - delt(hours=24 * (calendar.mdays[n.month-1] + 2))),
131 self.assertEqual(age(n - delt(hours=24 * (calendar.mdays[n.month-1] + 2))),
132 u'1 month and 2 days ago')
132 u'1 month and 2 days ago')
133 self.assertEqual(age(n - delt(hours=24 * 400)), u'1 year and 1 month ago')
133 self.assertEqual(age(n - delt(hours=24 * 400)), u'1 year and 1 month ago')
134
135 def test_tag_exctrator(self):
136 sample = (
137 "hello pta[tag] gog [[]] [[] sda ero[or]d [me =>>< sa]"
138 "[requires] [stale] [see<>=>] [see => http://url.com]"
139 "[requires => url] [lang => python] [just a tag]"
140 "[,d] [ => ULR ] [obsolete] [desc]]"
141 )
142 from rhodecode.lib.helpers import desc_stylize
143 res = desc_stylize(sample)
144 self.assertTrue('<div class="metatag" tag="tag">tag</div>' in res)
145 self.assertTrue('<div class="metatag" tag="obsolete">obsolete</div>' in res)
146 self.assertTrue('<div class="metatag" tag="stale">stale</div>' in res)
147 self.assertTrue('<div class="metatag" tag="lang">python</div>' in res)
148 self.assertTrue('<div class="metatag" tag="requires">requires =&gt; <a href="/url">url</a></div>' in res)
149 self.assertTrue('<div class="metatag" tag="tag">tag</div>' in res)
General Comments 0
You need to be logged in to leave comments. Login now