##// END OF EJS Templates
Implemented #379 defaults settings page for creation of repositories...
marcink -
r3056:6104dfd3 beta
parent child Browse files
Show More
@@ -0,0 +1,130 b''
1 # -*- coding: utf-8 -*-
2 """
3 rhodecode.controllers.admin.defaults
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
6 default settings controller for Rhodecode
7
8 :created_on: Apr 27, 2010
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
12 """
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
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
17 #
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
22 #
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/>.
25
26 import logging
27 import traceback
28 import formencode
29 from formencode import htmlfill
30
31 from pylons import request, session, tmpl_context as c, url
32 from pylons.controllers.util import abort, redirect
33 from pylons.i18n.translation import _
34
35 from rhodecode.lib import helpers as h
36 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
37 from rhodecode.lib.base import BaseController, render
38 from rhodecode.model.forms import DefaultsForm
39 from rhodecode.model.meta import Session
40 from rhodecode import BACKENDS
41 from rhodecode.model.db import RhodeCodeSetting
42
43 log = logging.getLogger(__name__)
44
45
46 class DefaultsController(BaseController):
47 """REST Controller styled on the Atom Publishing Protocol"""
48 # To properly map this controller, ensure your config/routing.py
49 # file has a resource setup:
50 # map.resource('default', 'defaults')
51
52 @LoginRequired()
53 @HasPermissionAllDecorator('hg.admin')
54 def __before__(self):
55 super(DefaultsController, self).__before__()
56
57 def index(self, format='html'):
58 """GET /defaults: All items in the collection"""
59 # url('defaults')
60 c.backends = BACKENDS.keys()
61 defaults = RhodeCodeSetting.get_default_repo_settings()
62
63 return htmlfill.render(
64 render('admin/defaults/defaults.html'),
65 defaults=defaults,
66 encoding="UTF-8",
67 force_defaults=False
68 )
69
70 def create(self):
71 """POST /defaults: Create a new item"""
72 # url('defaults')
73
74 def new(self, format='html'):
75 """GET /defaults/new: Form to create a new item"""
76 # url('new_default')
77
78 def update(self, id):
79 """PUT /defaults/id: Update an existing item"""
80 # Forms posted to this method should contain a hidden field:
81 # <input type="hidden" name="_method" value="PUT" />
82 # Or using helpers:
83 # h.form(url('default', id=ID),
84 # method='put')
85 # url('default', id=ID)
86
87 _form = DefaultsForm()()
88
89 try:
90 form_result = _form.to_python(dict(request.POST))
91 for k, v in form_result.iteritems():
92 setting = RhodeCodeSetting.get_by_name_or_create(k)
93 setting.app_settings_value = v
94 Session().add(setting)
95 Session().commit()
96 h.flash(_('Default settings updated successfully'),
97 category='success')
98
99 except formencode.Invalid, errors:
100 defaults = errors.value
101
102 return htmlfill.render(
103 render('admin/defaults/defaults.html'),
104 defaults=defaults,
105 errors=errors.error_dict or {},
106 prefix_error=False,
107 encoding="UTF-8")
108 except Exception:
109 log.error(traceback.format_exc())
110 h.flash(_('error occurred during update of defaults'),
111 category='error')
112
113 return redirect(url('defaults'))
114
115 def delete(self, id):
116 """DELETE /defaults/id: Delete an existing item"""
117 # Forms posted to this method should contain a hidden field:
118 # <input type="hidden" name="_method" value="DELETE" />
119 # Or using helpers:
120 # h.form(url('default', id=ID),
121 # method='delete')
122 # url('default', id=ID)
123
124 def show(self, id, format='html'):
125 """GET /defaults/id: Show a specific item"""
126 # url('default', id=ID)
127
128 def edit(self, id, format='html'):
129 """GET /defaults/id/edit: Form to edit an existing item"""
130 # url('edit_default', id=ID)
@@ -0,0 +1,93 b''
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
3
4 <%def name="title()">
5 ${_('Repositories defaults')} - ${c.rhodecode_name}
6 </%def>
7
8 <%def name="breadcrumbs_links()">
9 ${h.link_to(_('Admin'),h.url('admin_home'))}
10 &raquo;
11 ${_('Defaults')}
12 </%def>
13
14 <%def name="page_nav()">
15 ${self.menu('admin')}
16 </%def>
17
18 <%def name="main()">
19 <div class="box">
20 <!-- box / title -->
21 <div class="title">
22 ${self.breadcrumbs()}
23 </div>
24
25 <h3>${_('Repositories defaults')}</h3>
26
27 ${h.form(url('default', id='defaults'),method='put')}
28 <div class="form">
29 <!-- fields -->
30
31 <div class="fields">
32
33 <div class="field">
34 <div class="label">
35 <label for="default_repo_type">${_('Type')}:</label>
36 </div>
37 <div class="input">
38 ${h.select('default_repo_type','hg',c.backends,class_="medium")}
39 </div>
40 </div>
41
42 <div class="field">
43 <div class="label label-checkbox">
44 <label for="default_repo_private">${_('Private repository')}:</label>
45 </div>
46 <div class="checkboxes">
47 ${h.checkbox('default_repo_private',value="True")}
48 <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span>
49 </div>
50 </div>
51
52
53 <div class="field">
54 <div class="label label-checkbox">
55 <label for="default_repo_enable_statistics">${_('Enable statistics')}:</label>
56 </div>
57 <div class="checkboxes">
58 ${h.checkbox('default_repo_enable_statistics',value="True")}
59 <span class="help-block">${_('Enable statistics window on summary page.')}</span>
60 </div>
61 </div>
62
63 <div class="field">
64 <div class="label label-checkbox">
65 <label for="default_repo_enable_downloads">${_('Enable downloads')}:</label>
66 </div>
67 <div class="checkboxes">
68 ${h.checkbox('default_repo_enable_downloads',value="True")}
69 <span class="help-block">${_('Enable download menu on summary page.')}</span>
70 </div>
71 </div>
72
73 <div class="field">
74 <div class="label label-checkbox">
75 <label for="default_repo_enable_locking">${_('Enable locking')}:</label>
76 </div>
77 <div class="checkboxes">
78 ${h.checkbox('default_repo_enable_locking',value="True")}
79 <span class="help-block">${_('Enable lock-by-pulling on repository.')}</span>
80 </div>
81 </div>
82
83 <div class="buttons">
84 ${h.submit('save',_('Save'),class_="ui-btn large")}
85 </div>
86 </div>
87 </div>
88 ${h.end_form()}
89
90 ##<h3>${_('Groups defaults')}</h3>
91
92 </div>
93 </%def>
@@ -0,0 +1,72 b''
1 from rhodecode.tests import *
2 from rhodecode.model.db import RhodeCodeSetting
3
4
5 class TestDefaultsController(TestController):
6
7 def test_index(self):
8 self.log_user()
9 response = self.app.get(url('defaults'))
10 response.mustcontain('default_repo_private')
11 response.mustcontain('default_repo_enable_statistics')
12 response.mustcontain('default_repo_enable_downloads')
13 response.mustcontain('default_repo_enable_locking')
14
15 def test_index_as_xml(self):
16 response = self.app.get(url('formatted_defaults', format='xml'))
17
18 def test_create(self):
19 response = self.app.post(url('defaults'))
20
21 def test_new(self):
22 response = self.app.get(url('new_default'))
23
24 def test_new_as_xml(self):
25 response = self.app.get(url('formatted_new_default', format='xml'))
26
27 def test_update(self):
28 self.log_user()
29 params = {
30 'default_repo_enable_locking': True,
31 'default_repo_enable_downloads': True,
32 'default_repo_enable_statistics': True,
33 'default_repo_private': True,
34 'default_repo_type': 'hg',
35 }
36 response = self.app.put(url('default', id='default'), params=params)
37 self.checkSessionFlash(response, 'Default settings updated successfully')
38 defs = RhodeCodeSetting.get_default_repo_settings()
39 self.assertEqual(params, defs)
40
41 params = {
42 'default_repo_enable_locking': False,
43 'default_repo_enable_downloads': False,
44 'default_repo_enable_statistics': False,
45 'default_repo_private': False,
46 'default_repo_type': 'git',
47 }
48 response = self.app.put(url('default', id='default'), params=params)
49 self.checkSessionFlash(response, 'Default settings updated successfully')
50 defs = RhodeCodeSetting.get_default_repo_settings()
51 self.assertEqual(params, defs)
52
53 def test_update_browser_fakeout(self):
54 response = self.app.post(url('default', id=1), params=dict(_method='put'))
55
56 def test_delete(self):
57 response = self.app.delete(url('default', id=1))
58
59 def test_delete_browser_fakeout(self):
60 response = self.app.post(url('default', id=1), params=dict(_method='delete'))
61
62 def test_show(self):
63 response = self.app.get(url('default', id=1))
64
65 def test_show_as_xml(self):
66 response = self.app.get(url('formatted_default', id=1, format='xml'))
67
68 def test_edit(self):
69 response = self.app.get(url('edit_default', id=1))
70
71 def test_edit_as_xml(self):
72 response = self.app.get(url('formatted_edit_default', id=1, format='xml'))
@@ -1,620 +1,624 b''
1 """
1 """
2 Routes configuration
2 Routes configuration
3
3
4 The more specific and detailed routes should be defined first so they
4 The more specific and detailed routes should be defined first so they
5 may take precedent over the more generic routes. For more information
5 may take precedent over the more generic routes. For more information
6 refer to the routes manual at http://routes.groovie.org/docs/
6 refer to the routes manual at http://routes.groovie.org/docs/
7 """
7 """
8 from __future__ import with_statement
8 from __future__ import with_statement
9 from routes import Mapper
9 from routes import Mapper
10
10
11 # prefix for non repository related links needs to be prefixed with `/`
11 # prefix for non repository related links needs to be prefixed with `/`
12 ADMIN_PREFIX = '/_admin'
12 ADMIN_PREFIX = '/_admin'
13
13
14
14
15 def make_map(config):
15 def make_map(config):
16 """Create, configure and return the routes Mapper"""
16 """Create, configure and return the routes Mapper"""
17 rmap = Mapper(directory=config['pylons.paths']['controllers'],
17 rmap = Mapper(directory=config['pylons.paths']['controllers'],
18 always_scan=config['debug'])
18 always_scan=config['debug'])
19 rmap.minimization = False
19 rmap.minimization = False
20 rmap.explicit = False
20 rmap.explicit = False
21
21
22 from rhodecode.lib.utils import is_valid_repo
22 from rhodecode.lib.utils import is_valid_repo
23 from rhodecode.lib.utils import is_valid_repos_group
23 from rhodecode.lib.utils import is_valid_repos_group
24
24
25 def check_repo(environ, match_dict):
25 def check_repo(environ, match_dict):
26 """
26 """
27 check for valid repository for proper 404 handling
27 check for valid repository for proper 404 handling
28
28
29 :param environ:
29 :param environ:
30 :param match_dict:
30 :param match_dict:
31 """
31 """
32 from rhodecode.model.db import Repository
32 from rhodecode.model.db import Repository
33 repo_name = match_dict.get('repo_name')
33 repo_name = match_dict.get('repo_name')
34
34
35 if match_dict.get('f_path'):
35 if match_dict.get('f_path'):
36 #fix for multiple initial slashes that causes errors
36 #fix for multiple initial slashes that causes errors
37 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
37 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
38
38
39 try:
39 try:
40 by_id = repo_name.split('_')
40 by_id = repo_name.split('_')
41 if len(by_id) == 2 and by_id[1].isdigit() and by_id[0] == '':
41 if len(by_id) == 2 and by_id[1].isdigit() and by_id[0] == '':
42 repo_name = Repository.get(by_id[1]).repo_name
42 repo_name = Repository.get(by_id[1]).repo_name
43 match_dict['repo_name'] = repo_name
43 match_dict['repo_name'] = repo_name
44 except:
44 except:
45 pass
45 pass
46
46
47 return is_valid_repo(repo_name, config['base_path'])
47 return is_valid_repo(repo_name, config['base_path'])
48
48
49 def check_group(environ, match_dict):
49 def check_group(environ, match_dict):
50 """
50 """
51 check for valid repositories group for proper 404 handling
51 check for valid repositories group for proper 404 handling
52
52
53 :param environ:
53 :param environ:
54 :param match_dict:
54 :param match_dict:
55 """
55 """
56 repos_group_name = match_dict.get('group_name')
56 repos_group_name = match_dict.get('group_name')
57
57
58 return is_valid_repos_group(repos_group_name, config['base_path'])
58 return is_valid_repos_group(repos_group_name, config['base_path'])
59
59
60 def check_int(environ, match_dict):
60 def check_int(environ, match_dict):
61 return match_dict.get('id').isdigit()
61 return match_dict.get('id').isdigit()
62
62
63 # The ErrorController route (handles 404/500 error pages); it should
63 # The ErrorController route (handles 404/500 error pages); it should
64 # likely stay at the top, ensuring it can always be resolved
64 # likely stay at the top, ensuring it can always be resolved
65 rmap.connect('/error/{action}', controller='error')
65 rmap.connect('/error/{action}', controller='error')
66 rmap.connect('/error/{action}/{id}', controller='error')
66 rmap.connect('/error/{action}/{id}', controller='error')
67
67
68 #==========================================================================
68 #==========================================================================
69 # CUSTOM ROUTES HERE
69 # CUSTOM ROUTES HERE
70 #==========================================================================
70 #==========================================================================
71
71
72 #MAIN PAGE
72 #MAIN PAGE
73 rmap.connect('home', '/', controller='home', action='index')
73 rmap.connect('home', '/', controller='home', action='index')
74 rmap.connect('repo_switcher', '/repos', controller='home',
74 rmap.connect('repo_switcher', '/repos', controller='home',
75 action='repo_switcher')
75 action='repo_switcher')
76 rmap.connect('branch_tag_switcher', '/branches-tags/{repo_name:.*?}',
76 rmap.connect('branch_tag_switcher', '/branches-tags/{repo_name:.*?}',
77 controller='home', action='branch_tag_switcher')
77 controller='home', action='branch_tag_switcher')
78 rmap.connect('bugtracker',
78 rmap.connect('bugtracker',
79 "http://bitbucket.org/marcinkuzminski/rhodecode/issues",
79 "http://bitbucket.org/marcinkuzminski/rhodecode/issues",
80 _static=True)
80 _static=True)
81 rmap.connect('rst_help',
81 rmap.connect('rst_help',
82 "http://docutils.sourceforge.net/docs/user/rst/quickref.html",
82 "http://docutils.sourceforge.net/docs/user/rst/quickref.html",
83 _static=True)
83 _static=True)
84 rmap.connect('rhodecode_official', "http://rhodecode.org", _static=True)
84 rmap.connect('rhodecode_official', "http://rhodecode.org", _static=True)
85
85
86 #ADMIN REPOSITORY REST ROUTES
86 #ADMIN REPOSITORY REST ROUTES
87 with rmap.submapper(path_prefix=ADMIN_PREFIX,
87 with rmap.submapper(path_prefix=ADMIN_PREFIX,
88 controller='admin/repos') as m:
88 controller='admin/repos') as m:
89 m.connect("repos", "/repos",
89 m.connect("repos", "/repos",
90 action="create", conditions=dict(method=["POST"]))
90 action="create", conditions=dict(method=["POST"]))
91 m.connect("repos", "/repos",
91 m.connect("repos", "/repos",
92 action="index", conditions=dict(method=["GET"]))
92 action="index", conditions=dict(method=["GET"]))
93 m.connect("formatted_repos", "/repos.{format}",
93 m.connect("formatted_repos", "/repos.{format}",
94 action="index",
94 action="index",
95 conditions=dict(method=["GET"]))
95 conditions=dict(method=["GET"]))
96 m.connect("new_repo", "/repos/new",
96 m.connect("new_repo", "/repos/new",
97 action="new", conditions=dict(method=["GET"]))
97 action="new", conditions=dict(method=["GET"]))
98 m.connect("formatted_new_repo", "/repos/new.{format}",
98 m.connect("formatted_new_repo", "/repos/new.{format}",
99 action="new", conditions=dict(method=["GET"]))
99 action="new", conditions=dict(method=["GET"]))
100 m.connect("/repos/{repo_name:.*?}",
100 m.connect("/repos/{repo_name:.*?}",
101 action="update", conditions=dict(method=["PUT"],
101 action="update", conditions=dict(method=["PUT"],
102 function=check_repo))
102 function=check_repo))
103 m.connect("/repos/{repo_name:.*?}",
103 m.connect("/repos/{repo_name:.*?}",
104 action="delete", conditions=dict(method=["DELETE"],
104 action="delete", conditions=dict(method=["DELETE"],
105 function=check_repo))
105 function=check_repo))
106 m.connect("edit_repo", "/repos/{repo_name:.*?}/edit",
106 m.connect("edit_repo", "/repos/{repo_name:.*?}/edit",
107 action="edit", conditions=dict(method=["GET"],
107 action="edit", conditions=dict(method=["GET"],
108 function=check_repo))
108 function=check_repo))
109 m.connect("formatted_edit_repo", "/repos/{repo_name:.*?}.{format}/edit",
109 m.connect("formatted_edit_repo", "/repos/{repo_name:.*?}.{format}/edit",
110 action="edit", conditions=dict(method=["GET"],
110 action="edit", conditions=dict(method=["GET"],
111 function=check_repo))
111 function=check_repo))
112 m.connect("repo", "/repos/{repo_name:.*?}",
112 m.connect("repo", "/repos/{repo_name:.*?}",
113 action="show", conditions=dict(method=["GET"],
113 action="show", conditions=dict(method=["GET"],
114 function=check_repo))
114 function=check_repo))
115 m.connect("formatted_repo", "/repos/{repo_name:.*?}.{format}",
115 m.connect("formatted_repo", "/repos/{repo_name:.*?}.{format}",
116 action="show", conditions=dict(method=["GET"],
116 action="show", conditions=dict(method=["GET"],
117 function=check_repo))
117 function=check_repo))
118 #ajax delete repo perm user
118 #ajax delete repo perm user
119 m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*?}",
119 m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*?}",
120 action="delete_perm_user",
120 action="delete_perm_user",
121 conditions=dict(method=["DELETE"], function=check_repo))
121 conditions=dict(method=["DELETE"], function=check_repo))
122
122
123 #ajax delete repo perm users_group
123 #ajax delete repo perm users_group
124 m.connect('delete_repo_users_group',
124 m.connect('delete_repo_users_group',
125 "/repos_delete_users_group/{repo_name:.*?}",
125 "/repos_delete_users_group/{repo_name:.*?}",
126 action="delete_perm_users_group",
126 action="delete_perm_users_group",
127 conditions=dict(method=["DELETE"], function=check_repo))
127 conditions=dict(method=["DELETE"], function=check_repo))
128
128
129 #settings actions
129 #settings actions
130 m.connect('repo_stats', "/repos_stats/{repo_name:.*?}",
130 m.connect('repo_stats', "/repos_stats/{repo_name:.*?}",
131 action="repo_stats", conditions=dict(method=["DELETE"],
131 action="repo_stats", conditions=dict(method=["DELETE"],
132 function=check_repo))
132 function=check_repo))
133 m.connect('repo_cache', "/repos_cache/{repo_name:.*?}",
133 m.connect('repo_cache', "/repos_cache/{repo_name:.*?}",
134 action="repo_cache", conditions=dict(method=["DELETE"],
134 action="repo_cache", conditions=dict(method=["DELETE"],
135 function=check_repo))
135 function=check_repo))
136 m.connect('repo_public_journal', "/repos_public_journal/{repo_name:.*?}",
136 m.connect('repo_public_journal', "/repos_public_journal/{repo_name:.*?}",
137 action="repo_public_journal", conditions=dict(method=["PUT"],
137 action="repo_public_journal", conditions=dict(method=["PUT"],
138 function=check_repo))
138 function=check_repo))
139 m.connect('repo_pull', "/repo_pull/{repo_name:.*?}",
139 m.connect('repo_pull', "/repo_pull/{repo_name:.*?}",
140 action="repo_pull", conditions=dict(method=["PUT"],
140 action="repo_pull", conditions=dict(method=["PUT"],
141 function=check_repo))
141 function=check_repo))
142 m.connect('repo_as_fork', "/repo_as_fork/{repo_name:.*?}",
142 m.connect('repo_as_fork', "/repo_as_fork/{repo_name:.*?}",
143 action="repo_as_fork", conditions=dict(method=["PUT"],
143 action="repo_as_fork", conditions=dict(method=["PUT"],
144 function=check_repo))
144 function=check_repo))
145 m.connect('repo_locking', "/repo_locking/{repo_name:.*?}",
145 m.connect('repo_locking', "/repo_locking/{repo_name:.*?}",
146 action="repo_locking", conditions=dict(method=["PUT"],
146 action="repo_locking", conditions=dict(method=["PUT"],
147 function=check_repo))
147 function=check_repo))
148
148
149 with rmap.submapper(path_prefix=ADMIN_PREFIX,
149 with rmap.submapper(path_prefix=ADMIN_PREFIX,
150 controller='admin/repos_groups') as m:
150 controller='admin/repos_groups') as m:
151 m.connect("repos_groups", "/repos_groups",
151 m.connect("repos_groups", "/repos_groups",
152 action="create", conditions=dict(method=["POST"]))
152 action="create", conditions=dict(method=["POST"]))
153 m.connect("repos_groups", "/repos_groups",
153 m.connect("repos_groups", "/repos_groups",
154 action="index", conditions=dict(method=["GET"]))
154 action="index", conditions=dict(method=["GET"]))
155 m.connect("formatted_repos_groups", "/repos_groups.{format}",
155 m.connect("formatted_repos_groups", "/repos_groups.{format}",
156 action="index", conditions=dict(method=["GET"]))
156 action="index", conditions=dict(method=["GET"]))
157 m.connect("new_repos_group", "/repos_groups/new",
157 m.connect("new_repos_group", "/repos_groups/new",
158 action="new", conditions=dict(method=["GET"]))
158 action="new", conditions=dict(method=["GET"]))
159 m.connect("formatted_new_repos_group", "/repos_groups/new.{format}",
159 m.connect("formatted_new_repos_group", "/repos_groups/new.{format}",
160 action="new", conditions=dict(method=["GET"]))
160 action="new", conditions=dict(method=["GET"]))
161 m.connect("update_repos_group", "/repos_groups/{id}",
161 m.connect("update_repos_group", "/repos_groups/{id}",
162 action="update", conditions=dict(method=["PUT"],
162 action="update", conditions=dict(method=["PUT"],
163 function=check_int))
163 function=check_int))
164 m.connect("delete_repos_group", "/repos_groups/{id}",
164 m.connect("delete_repos_group", "/repos_groups/{id}",
165 action="delete", conditions=dict(method=["DELETE"],
165 action="delete", conditions=dict(method=["DELETE"],
166 function=check_int))
166 function=check_int))
167 m.connect("edit_repos_group", "/repos_groups/{id:.*?}/edit",
167 m.connect("edit_repos_group", "/repos_groups/{id:.*?}/edit",
168 action="edit", conditions=dict(method=["GET"],))
168 action="edit", conditions=dict(method=["GET"],))
169 m.connect("formatted_edit_repos_group",
169 m.connect("formatted_edit_repos_group",
170 "/repos_groups/{id}.{format}/edit",
170 "/repos_groups/{id}.{format}/edit",
171 action="edit", conditions=dict(method=["GET"],
171 action="edit", conditions=dict(method=["GET"],
172 function=check_int))
172 function=check_int))
173 m.connect("repos_group", "/repos_groups/{id}",
173 m.connect("repos_group", "/repos_groups/{id}",
174 action="show", conditions=dict(method=["GET"],
174 action="show", conditions=dict(method=["GET"],
175 function=check_int))
175 function=check_int))
176 m.connect("formatted_repos_group", "/repos_groups/{id}.{format}",
176 m.connect("formatted_repos_group", "/repos_groups/{id}.{format}",
177 action="show", conditions=dict(method=["GET"],
177 action="show", conditions=dict(method=["GET"],
178 function=check_int))
178 function=check_int))
179 # ajax delete repos group perm user
179 # ajax delete repos group perm user
180 m.connect('delete_repos_group_user_perm',
180 m.connect('delete_repos_group_user_perm',
181 "/delete_repos_group_user_perm/{group_name:.*}",
181 "/delete_repos_group_user_perm/{group_name:.*}",
182 action="delete_repos_group_user_perm",
182 action="delete_repos_group_user_perm",
183 conditions=dict(method=["DELETE"], function=check_group))
183 conditions=dict(method=["DELETE"], function=check_group))
184
184
185 # ajax delete repos group perm users_group
185 # ajax delete repos group perm users_group
186 m.connect('delete_repos_group_users_group_perm',
186 m.connect('delete_repos_group_users_group_perm',
187 "/delete_repos_group_users_group_perm/{group_name:.*}",
187 "/delete_repos_group_users_group_perm/{group_name:.*}",
188 action="delete_repos_group_users_group_perm",
188 action="delete_repos_group_users_group_perm",
189 conditions=dict(method=["DELETE"], function=check_group))
189 conditions=dict(method=["DELETE"], function=check_group))
190
190
191 #ADMIN USER REST ROUTES
191 #ADMIN USER REST ROUTES
192 with rmap.submapper(path_prefix=ADMIN_PREFIX,
192 with rmap.submapper(path_prefix=ADMIN_PREFIX,
193 controller='admin/users') as m:
193 controller='admin/users') as m:
194 m.connect("users", "/users",
194 m.connect("users", "/users",
195 action="create", conditions=dict(method=["POST"]))
195 action="create", conditions=dict(method=["POST"]))
196 m.connect("users", "/users",
196 m.connect("users", "/users",
197 action="index", conditions=dict(method=["GET"]))
197 action="index", conditions=dict(method=["GET"]))
198 m.connect("formatted_users", "/users.{format}",
198 m.connect("formatted_users", "/users.{format}",
199 action="index", conditions=dict(method=["GET"]))
199 action="index", conditions=dict(method=["GET"]))
200 m.connect("new_user", "/users/new",
200 m.connect("new_user", "/users/new",
201 action="new", conditions=dict(method=["GET"]))
201 action="new", conditions=dict(method=["GET"]))
202 m.connect("formatted_new_user", "/users/new.{format}",
202 m.connect("formatted_new_user", "/users/new.{format}",
203 action="new", conditions=dict(method=["GET"]))
203 action="new", conditions=dict(method=["GET"]))
204 m.connect("update_user", "/users/{id}",
204 m.connect("update_user", "/users/{id}",
205 action="update", conditions=dict(method=["PUT"]))
205 action="update", conditions=dict(method=["PUT"]))
206 m.connect("delete_user", "/users/{id}",
206 m.connect("delete_user", "/users/{id}",
207 action="delete", conditions=dict(method=["DELETE"]))
207 action="delete", conditions=dict(method=["DELETE"]))
208 m.connect("edit_user", "/users/{id}/edit",
208 m.connect("edit_user", "/users/{id}/edit",
209 action="edit", conditions=dict(method=["GET"]))
209 action="edit", conditions=dict(method=["GET"]))
210 m.connect("formatted_edit_user",
210 m.connect("formatted_edit_user",
211 "/users/{id}.{format}/edit",
211 "/users/{id}.{format}/edit",
212 action="edit", conditions=dict(method=["GET"]))
212 action="edit", conditions=dict(method=["GET"]))
213 m.connect("user", "/users/{id}",
213 m.connect("user", "/users/{id}",
214 action="show", conditions=dict(method=["GET"]))
214 action="show", conditions=dict(method=["GET"]))
215 m.connect("formatted_user", "/users/{id}.{format}",
215 m.connect("formatted_user", "/users/{id}.{format}",
216 action="show", conditions=dict(method=["GET"]))
216 action="show", conditions=dict(method=["GET"]))
217
217
218 #EXTRAS USER ROUTES
218 #EXTRAS USER ROUTES
219 m.connect("user_perm", "/users_perm/{id}",
219 m.connect("user_perm", "/users_perm/{id}",
220 action="update_perm", conditions=dict(method=["PUT"]))
220 action="update_perm", conditions=dict(method=["PUT"]))
221 m.connect("user_emails", "/users_emails/{id}",
221 m.connect("user_emails", "/users_emails/{id}",
222 action="add_email", conditions=dict(method=["PUT"]))
222 action="add_email", conditions=dict(method=["PUT"]))
223 m.connect("user_emails_delete", "/users_emails/{id}",
223 m.connect("user_emails_delete", "/users_emails/{id}",
224 action="delete_email", conditions=dict(method=["DELETE"]))
224 action="delete_email", conditions=dict(method=["DELETE"]))
225
225
226 #ADMIN USERS GROUPS REST ROUTES
226 #ADMIN USERS GROUPS REST ROUTES
227 with rmap.submapper(path_prefix=ADMIN_PREFIX,
227 with rmap.submapper(path_prefix=ADMIN_PREFIX,
228 controller='admin/users_groups') as m:
228 controller='admin/users_groups') as m:
229 m.connect("users_groups", "/users_groups",
229 m.connect("users_groups", "/users_groups",
230 action="create", conditions=dict(method=["POST"]))
230 action="create", conditions=dict(method=["POST"]))
231 m.connect("users_groups", "/users_groups",
231 m.connect("users_groups", "/users_groups",
232 action="index", conditions=dict(method=["GET"]))
232 action="index", conditions=dict(method=["GET"]))
233 m.connect("formatted_users_groups", "/users_groups.{format}",
233 m.connect("formatted_users_groups", "/users_groups.{format}",
234 action="index", conditions=dict(method=["GET"]))
234 action="index", conditions=dict(method=["GET"]))
235 m.connect("new_users_group", "/users_groups/new",
235 m.connect("new_users_group", "/users_groups/new",
236 action="new", conditions=dict(method=["GET"]))
236 action="new", conditions=dict(method=["GET"]))
237 m.connect("formatted_new_users_group", "/users_groups/new.{format}",
237 m.connect("formatted_new_users_group", "/users_groups/new.{format}",
238 action="new", conditions=dict(method=["GET"]))
238 action="new", conditions=dict(method=["GET"]))
239 m.connect("update_users_group", "/users_groups/{id}",
239 m.connect("update_users_group", "/users_groups/{id}",
240 action="update", conditions=dict(method=["PUT"]))
240 action="update", conditions=dict(method=["PUT"]))
241 m.connect("delete_users_group", "/users_groups/{id}",
241 m.connect("delete_users_group", "/users_groups/{id}",
242 action="delete", conditions=dict(method=["DELETE"]))
242 action="delete", conditions=dict(method=["DELETE"]))
243 m.connect("edit_users_group", "/users_groups/{id}/edit",
243 m.connect("edit_users_group", "/users_groups/{id}/edit",
244 action="edit", conditions=dict(method=["GET"]))
244 action="edit", conditions=dict(method=["GET"]))
245 m.connect("formatted_edit_users_group",
245 m.connect("formatted_edit_users_group",
246 "/users_groups/{id}.{format}/edit",
246 "/users_groups/{id}.{format}/edit",
247 action="edit", conditions=dict(method=["GET"]))
247 action="edit", conditions=dict(method=["GET"]))
248 m.connect("users_group", "/users_groups/{id}",
248 m.connect("users_group", "/users_groups/{id}",
249 action="show", conditions=dict(method=["GET"]))
249 action="show", conditions=dict(method=["GET"]))
250 m.connect("formatted_users_group", "/users_groups/{id}.{format}",
250 m.connect("formatted_users_group", "/users_groups/{id}.{format}",
251 action="show", conditions=dict(method=["GET"]))
251 action="show", conditions=dict(method=["GET"]))
252
252
253 #EXTRAS USER ROUTES
253 #EXTRAS USER ROUTES
254 m.connect("users_group_perm", "/users_groups_perm/{id}",
254 m.connect("users_group_perm", "/users_groups_perm/{id}",
255 action="update_perm", conditions=dict(method=["PUT"]))
255 action="update_perm", conditions=dict(method=["PUT"]))
256
256
257 #ADMIN GROUP REST ROUTES
257 #ADMIN GROUP REST ROUTES
258 rmap.resource('group', 'groups',
258 rmap.resource('group', 'groups',
259 controller='admin/groups', path_prefix=ADMIN_PREFIX)
259 controller='admin/groups', path_prefix=ADMIN_PREFIX)
260
260
261 #ADMIN PERMISSIONS REST ROUTES
261 #ADMIN PERMISSIONS REST ROUTES
262 rmap.resource('permission', 'permissions',
262 rmap.resource('permission', 'permissions',
263 controller='admin/permissions', path_prefix=ADMIN_PREFIX)
263 controller='admin/permissions', path_prefix=ADMIN_PREFIX)
264
264
265 #ADMIN DEFAULTS REST ROUTES
266 rmap.resource('default', 'defaults',
267 controller='admin/defaults', path_prefix=ADMIN_PREFIX)
268
265 ##ADMIN LDAP SETTINGS
269 ##ADMIN LDAP SETTINGS
266 rmap.connect('ldap_settings', '%s/ldap' % ADMIN_PREFIX,
270 rmap.connect('ldap_settings', '%s/ldap' % ADMIN_PREFIX,
267 controller='admin/ldap_settings', action='ldap_settings',
271 controller='admin/ldap_settings', action='ldap_settings',
268 conditions=dict(method=["POST"]))
272 conditions=dict(method=["POST"]))
269
273
270 rmap.connect('ldap_home', '%s/ldap' % ADMIN_PREFIX,
274 rmap.connect('ldap_home', '%s/ldap' % ADMIN_PREFIX,
271 controller='admin/ldap_settings')
275 controller='admin/ldap_settings')
272
276
273 #ADMIN SETTINGS REST ROUTES
277 #ADMIN SETTINGS REST ROUTES
274 with rmap.submapper(path_prefix=ADMIN_PREFIX,
278 with rmap.submapper(path_prefix=ADMIN_PREFIX,
275 controller='admin/settings') as m:
279 controller='admin/settings') as m:
276 m.connect("admin_settings", "/settings",
280 m.connect("admin_settings", "/settings",
277 action="create", conditions=dict(method=["POST"]))
281 action="create", conditions=dict(method=["POST"]))
278 m.connect("admin_settings", "/settings",
282 m.connect("admin_settings", "/settings",
279 action="index", conditions=dict(method=["GET"]))
283 action="index", conditions=dict(method=["GET"]))
280 m.connect("formatted_admin_settings", "/settings.{format}",
284 m.connect("formatted_admin_settings", "/settings.{format}",
281 action="index", conditions=dict(method=["GET"]))
285 action="index", conditions=dict(method=["GET"]))
282 m.connect("admin_new_setting", "/settings/new",
286 m.connect("admin_new_setting", "/settings/new",
283 action="new", conditions=dict(method=["GET"]))
287 action="new", conditions=dict(method=["GET"]))
284 m.connect("formatted_admin_new_setting", "/settings/new.{format}",
288 m.connect("formatted_admin_new_setting", "/settings/new.{format}",
285 action="new", conditions=dict(method=["GET"]))
289 action="new", conditions=dict(method=["GET"]))
286 m.connect("/settings/{setting_id}",
290 m.connect("/settings/{setting_id}",
287 action="update", conditions=dict(method=["PUT"]))
291 action="update", conditions=dict(method=["PUT"]))
288 m.connect("/settings/{setting_id}",
292 m.connect("/settings/{setting_id}",
289 action="delete", conditions=dict(method=["DELETE"]))
293 action="delete", conditions=dict(method=["DELETE"]))
290 m.connect("admin_edit_setting", "/settings/{setting_id}/edit",
294 m.connect("admin_edit_setting", "/settings/{setting_id}/edit",
291 action="edit", conditions=dict(method=["GET"]))
295 action="edit", conditions=dict(method=["GET"]))
292 m.connect("formatted_admin_edit_setting",
296 m.connect("formatted_admin_edit_setting",
293 "/settings/{setting_id}.{format}/edit",
297 "/settings/{setting_id}.{format}/edit",
294 action="edit", conditions=dict(method=["GET"]))
298 action="edit", conditions=dict(method=["GET"]))
295 m.connect("admin_setting", "/settings/{setting_id}",
299 m.connect("admin_setting", "/settings/{setting_id}",
296 action="show", conditions=dict(method=["GET"]))
300 action="show", conditions=dict(method=["GET"]))
297 m.connect("formatted_admin_setting", "/settings/{setting_id}.{format}",
301 m.connect("formatted_admin_setting", "/settings/{setting_id}.{format}",
298 action="show", conditions=dict(method=["GET"]))
302 action="show", conditions=dict(method=["GET"]))
299 m.connect("admin_settings_my_account", "/my_account",
303 m.connect("admin_settings_my_account", "/my_account",
300 action="my_account", conditions=dict(method=["GET"]))
304 action="my_account", conditions=dict(method=["GET"]))
301 m.connect("admin_settings_my_account_update", "/my_account_update",
305 m.connect("admin_settings_my_account_update", "/my_account_update",
302 action="my_account_update", conditions=dict(method=["PUT"]))
306 action="my_account_update", conditions=dict(method=["PUT"]))
303 m.connect("admin_settings_create_repository", "/create_repository",
307 m.connect("admin_settings_create_repository", "/create_repository",
304 action="create_repository", conditions=dict(method=["GET"]))
308 action="create_repository", conditions=dict(method=["GET"]))
305 m.connect("admin_settings_my_repos", "/my_account/repos",
309 m.connect("admin_settings_my_repos", "/my_account/repos",
306 action="my_account_my_repos", conditions=dict(method=["GET"]))
310 action="my_account_my_repos", conditions=dict(method=["GET"]))
307 m.connect("admin_settings_my_pullrequests", "/my_account/pull_requests",
311 m.connect("admin_settings_my_pullrequests", "/my_account/pull_requests",
308 action="my_account_my_pullrequests", conditions=dict(method=["GET"]))
312 action="my_account_my_pullrequests", conditions=dict(method=["GET"]))
309
313
310 #NOTIFICATION REST ROUTES
314 #NOTIFICATION REST ROUTES
311 with rmap.submapper(path_prefix=ADMIN_PREFIX,
315 with rmap.submapper(path_prefix=ADMIN_PREFIX,
312 controller='admin/notifications') as m:
316 controller='admin/notifications') as m:
313 m.connect("notifications", "/notifications",
317 m.connect("notifications", "/notifications",
314 action="create", conditions=dict(method=["POST"]))
318 action="create", conditions=dict(method=["POST"]))
315 m.connect("notifications", "/notifications",
319 m.connect("notifications", "/notifications",
316 action="index", conditions=dict(method=["GET"]))
320 action="index", conditions=dict(method=["GET"]))
317 m.connect("notifications_mark_all_read", "/notifications/mark_all_read",
321 m.connect("notifications_mark_all_read", "/notifications/mark_all_read",
318 action="mark_all_read", conditions=dict(method=["GET"]))
322 action="mark_all_read", conditions=dict(method=["GET"]))
319 m.connect("formatted_notifications", "/notifications.{format}",
323 m.connect("formatted_notifications", "/notifications.{format}",
320 action="index", conditions=dict(method=["GET"]))
324 action="index", conditions=dict(method=["GET"]))
321 m.connect("new_notification", "/notifications/new",
325 m.connect("new_notification", "/notifications/new",
322 action="new", conditions=dict(method=["GET"]))
326 action="new", conditions=dict(method=["GET"]))
323 m.connect("formatted_new_notification", "/notifications/new.{format}",
327 m.connect("formatted_new_notification", "/notifications/new.{format}",
324 action="new", conditions=dict(method=["GET"]))
328 action="new", conditions=dict(method=["GET"]))
325 m.connect("/notification/{notification_id}",
329 m.connect("/notification/{notification_id}",
326 action="update", conditions=dict(method=["PUT"]))
330 action="update", conditions=dict(method=["PUT"]))
327 m.connect("/notification/{notification_id}",
331 m.connect("/notification/{notification_id}",
328 action="delete", conditions=dict(method=["DELETE"]))
332 action="delete", conditions=dict(method=["DELETE"]))
329 m.connect("edit_notification", "/notification/{notification_id}/edit",
333 m.connect("edit_notification", "/notification/{notification_id}/edit",
330 action="edit", conditions=dict(method=["GET"]))
334 action="edit", conditions=dict(method=["GET"]))
331 m.connect("formatted_edit_notification",
335 m.connect("formatted_edit_notification",
332 "/notification/{notification_id}.{format}/edit",
336 "/notification/{notification_id}.{format}/edit",
333 action="edit", conditions=dict(method=["GET"]))
337 action="edit", conditions=dict(method=["GET"]))
334 m.connect("notification", "/notification/{notification_id}",
338 m.connect("notification", "/notification/{notification_id}",
335 action="show", conditions=dict(method=["GET"]))
339 action="show", conditions=dict(method=["GET"]))
336 m.connect("formatted_notification", "/notifications/{notification_id}.{format}",
340 m.connect("formatted_notification", "/notifications/{notification_id}.{format}",
337 action="show", conditions=dict(method=["GET"]))
341 action="show", conditions=dict(method=["GET"]))
338
342
339 #ADMIN MAIN PAGES
343 #ADMIN MAIN PAGES
340 with rmap.submapper(path_prefix=ADMIN_PREFIX,
344 with rmap.submapper(path_prefix=ADMIN_PREFIX,
341 controller='admin/admin') as m:
345 controller='admin/admin') as m:
342 m.connect('admin_home', '', action='index')
346 m.connect('admin_home', '', action='index')
343 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
347 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
344 action='add_repo')
348 action='add_repo')
345
349
346 #==========================================================================
350 #==========================================================================
347 # API V2
351 # API V2
348 #==========================================================================
352 #==========================================================================
349 with rmap.submapper(path_prefix=ADMIN_PREFIX,
353 with rmap.submapper(path_prefix=ADMIN_PREFIX,
350 controller='api/api') as m:
354 controller='api/api') as m:
351 m.connect('api', '/api')
355 m.connect('api', '/api')
352
356
353 #USER JOURNAL
357 #USER JOURNAL
354 rmap.connect('journal_my_repos', '%s/journal_my_repos' % ADMIN_PREFIX,
358 rmap.connect('journal_my_repos', '%s/journal_my_repos' % ADMIN_PREFIX,
355 controller='journal', action='index_my_repos')
359 controller='journal', action='index_my_repos')
356 rmap.connect('journal', '%s/journal' % ADMIN_PREFIX,
360 rmap.connect('journal', '%s/journal' % ADMIN_PREFIX,
357 controller='journal', action='index')
361 controller='journal', action='index')
358 rmap.connect('journal_rss', '%s/journal/rss' % ADMIN_PREFIX,
362 rmap.connect('journal_rss', '%s/journal/rss' % ADMIN_PREFIX,
359 controller='journal', action='journal_rss')
363 controller='journal', action='journal_rss')
360 rmap.connect('journal_atom', '%s/journal/atom' % ADMIN_PREFIX,
364 rmap.connect('journal_atom', '%s/journal/atom' % ADMIN_PREFIX,
361 controller='journal', action='journal_atom')
365 controller='journal', action='journal_atom')
362
366
363 rmap.connect('public_journal', '%s/public_journal' % ADMIN_PREFIX,
367 rmap.connect('public_journal', '%s/public_journal' % ADMIN_PREFIX,
364 controller='journal', action="public_journal")
368 controller='journal', action="public_journal")
365
369
366 rmap.connect('public_journal_rss', '%s/public_journal/rss' % ADMIN_PREFIX,
370 rmap.connect('public_journal_rss', '%s/public_journal/rss' % ADMIN_PREFIX,
367 controller='journal', action="public_journal_rss")
371 controller='journal', action="public_journal_rss")
368
372
369 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % ADMIN_PREFIX,
373 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % ADMIN_PREFIX,
370 controller='journal', action="public_journal_rss")
374 controller='journal', action="public_journal_rss")
371
375
372 rmap.connect('public_journal_atom',
376 rmap.connect('public_journal_atom',
373 '%s/public_journal/atom' % ADMIN_PREFIX, controller='journal',
377 '%s/public_journal/atom' % ADMIN_PREFIX, controller='journal',
374 action="public_journal_atom")
378 action="public_journal_atom")
375
379
376 rmap.connect('public_journal_atom_old',
380 rmap.connect('public_journal_atom_old',
377 '%s/public_journal_atom' % ADMIN_PREFIX, controller='journal',
381 '%s/public_journal_atom' % ADMIN_PREFIX, controller='journal',
378 action="public_journal_atom")
382 action="public_journal_atom")
379
383
380 rmap.connect('toggle_following', '%s/toggle_following' % ADMIN_PREFIX,
384 rmap.connect('toggle_following', '%s/toggle_following' % ADMIN_PREFIX,
381 controller='journal', action='toggle_following',
385 controller='journal', action='toggle_following',
382 conditions=dict(method=["POST"]))
386 conditions=dict(method=["POST"]))
383
387
384 #SEARCH
388 #SEARCH
385 rmap.connect('search', '%s/search' % ADMIN_PREFIX, controller='search',)
389 rmap.connect('search', '%s/search' % ADMIN_PREFIX, controller='search',)
386 rmap.connect('search_repo', '%s/search/{search_repo:.*}' % ADMIN_PREFIX,
390 rmap.connect('search_repo', '%s/search/{search_repo:.*}' % ADMIN_PREFIX,
387 controller='search')
391 controller='search')
388
392
389 #LOGIN/LOGOUT/REGISTER/SIGN IN
393 #LOGIN/LOGOUT/REGISTER/SIGN IN
390 rmap.connect('login_home', '%s/login' % ADMIN_PREFIX, controller='login')
394 rmap.connect('login_home', '%s/login' % ADMIN_PREFIX, controller='login')
391 rmap.connect('logout_home', '%s/logout' % ADMIN_PREFIX, controller='login',
395 rmap.connect('logout_home', '%s/logout' % ADMIN_PREFIX, controller='login',
392 action='logout')
396 action='logout')
393
397
394 rmap.connect('register', '%s/register' % ADMIN_PREFIX, controller='login',
398 rmap.connect('register', '%s/register' % ADMIN_PREFIX, controller='login',
395 action='register')
399 action='register')
396
400
397 rmap.connect('reset_password', '%s/password_reset' % ADMIN_PREFIX,
401 rmap.connect('reset_password', '%s/password_reset' % ADMIN_PREFIX,
398 controller='login', action='password_reset')
402 controller='login', action='password_reset')
399
403
400 rmap.connect('reset_password_confirmation',
404 rmap.connect('reset_password_confirmation',
401 '%s/password_reset_confirmation' % ADMIN_PREFIX,
405 '%s/password_reset_confirmation' % ADMIN_PREFIX,
402 controller='login', action='password_reset_confirmation')
406 controller='login', action='password_reset_confirmation')
403
407
404 #FEEDS
408 #FEEDS
405 rmap.connect('rss_feed_home', '/{repo_name:.*?}/feed/rss',
409 rmap.connect('rss_feed_home', '/{repo_name:.*?}/feed/rss',
406 controller='feed', action='rss',
410 controller='feed', action='rss',
407 conditions=dict(function=check_repo))
411 conditions=dict(function=check_repo))
408
412
409 rmap.connect('atom_feed_home', '/{repo_name:.*?}/feed/atom',
413 rmap.connect('atom_feed_home', '/{repo_name:.*?}/feed/atom',
410 controller='feed', action='atom',
414 controller='feed', action='atom',
411 conditions=dict(function=check_repo))
415 conditions=dict(function=check_repo))
412
416
413 #==========================================================================
417 #==========================================================================
414 # REPOSITORY ROUTES
418 # REPOSITORY ROUTES
415 #==========================================================================
419 #==========================================================================
416 rmap.connect('summary_home', '/{repo_name:.*?}',
420 rmap.connect('summary_home', '/{repo_name:.*?}',
417 controller='summary',
421 controller='summary',
418 conditions=dict(function=check_repo))
422 conditions=dict(function=check_repo))
419
423
420 rmap.connect('repos_group_home', '/{group_name:.*}',
424 rmap.connect('repos_group_home', '/{group_name:.*}',
421 controller='admin/repos_groups', action="show_by_name",
425 controller='admin/repos_groups', action="show_by_name",
422 conditions=dict(function=check_group))
426 conditions=dict(function=check_group))
423
427
424 rmap.connect('changeset_home', '/{repo_name:.*?}/changeset/{revision}',
428 rmap.connect('changeset_home', '/{repo_name:.*?}/changeset/{revision}',
425 controller='changeset', revision='tip',
429 controller='changeset', revision='tip',
426 conditions=dict(function=check_repo))
430 conditions=dict(function=check_repo))
427
431
428 #still working url for backward compat.
432 #still working url for backward compat.
429 rmap.connect('raw_changeset_home_depraced',
433 rmap.connect('raw_changeset_home_depraced',
430 '/{repo_name:.*?}/raw-changeset/{revision}',
434 '/{repo_name:.*?}/raw-changeset/{revision}',
431 controller='changeset', action='changeset_raw',
435 controller='changeset', action='changeset_raw',
432 revision='tip', conditions=dict(function=check_repo))
436 revision='tip', conditions=dict(function=check_repo))
433
437
434 ## new URLs
438 ## new URLs
435 rmap.connect('changeset_raw_home',
439 rmap.connect('changeset_raw_home',
436 '/{repo_name:.*?}/changeset-diff/{revision}',
440 '/{repo_name:.*?}/changeset-diff/{revision}',
437 controller='changeset', action='changeset_raw',
441 controller='changeset', action='changeset_raw',
438 revision='tip', conditions=dict(function=check_repo))
442 revision='tip', conditions=dict(function=check_repo))
439
443
440 rmap.connect('changeset_patch_home',
444 rmap.connect('changeset_patch_home',
441 '/{repo_name:.*?}/changeset-patch/{revision}',
445 '/{repo_name:.*?}/changeset-patch/{revision}',
442 controller='changeset', action='changeset_patch',
446 controller='changeset', action='changeset_patch',
443 revision='tip', conditions=dict(function=check_repo))
447 revision='tip', conditions=dict(function=check_repo))
444
448
445 rmap.connect('changeset_download_home',
449 rmap.connect('changeset_download_home',
446 '/{repo_name:.*?}/changeset-download/{revision}',
450 '/{repo_name:.*?}/changeset-download/{revision}',
447 controller='changeset', action='changeset_download',
451 controller='changeset', action='changeset_download',
448 revision='tip', conditions=dict(function=check_repo))
452 revision='tip', conditions=dict(function=check_repo))
449
453
450 rmap.connect('changeset_comment',
454 rmap.connect('changeset_comment',
451 '/{repo_name:.*?}/changeset/{revision}/comment',
455 '/{repo_name:.*?}/changeset/{revision}/comment',
452 controller='changeset', revision='tip', action='comment',
456 controller='changeset', revision='tip', action='comment',
453 conditions=dict(function=check_repo))
457 conditions=dict(function=check_repo))
454
458
455 rmap.connect('changeset_comment_delete',
459 rmap.connect('changeset_comment_delete',
456 '/{repo_name:.*?}/changeset/comment/{comment_id}/delete',
460 '/{repo_name:.*?}/changeset/comment/{comment_id}/delete',
457 controller='changeset', action='delete_comment',
461 controller='changeset', action='delete_comment',
458 conditions=dict(function=check_repo, method=["DELETE"]))
462 conditions=dict(function=check_repo, method=["DELETE"]))
459
463
460 rmap.connect('changeset_info', '/changeset_info/{repo_name:.*?}/{revision}',
464 rmap.connect('changeset_info', '/changeset_info/{repo_name:.*?}/{revision}',
461 controller='changeset', action='changeset_info')
465 controller='changeset', action='changeset_info')
462
466
463 rmap.connect('compare_url',
467 rmap.connect('compare_url',
464 '/{repo_name:.*?}/compare/{org_ref_type}@{org_ref:.*?}...{other_ref_type}@{other_ref:.*?}',
468 '/{repo_name:.*?}/compare/{org_ref_type}@{org_ref:.*?}...{other_ref_type}@{other_ref:.*?}',
465 controller='compare', action='index',
469 controller='compare', action='index',
466 conditions=dict(function=check_repo),
470 conditions=dict(function=check_repo),
467 requirements=dict(
471 requirements=dict(
468 org_ref_type='(branch|book|tag|rev|org_ref_type)',
472 org_ref_type='(branch|book|tag|rev|org_ref_type)',
469 other_ref_type='(branch|book|tag|rev|other_ref_type)')
473 other_ref_type='(branch|book|tag|rev|other_ref_type)')
470 )
474 )
471
475
472 rmap.connect('pullrequest_home',
476 rmap.connect('pullrequest_home',
473 '/{repo_name:.*?}/pull-request/new', controller='pullrequests',
477 '/{repo_name:.*?}/pull-request/new', controller='pullrequests',
474 action='index', conditions=dict(function=check_repo,
478 action='index', conditions=dict(function=check_repo,
475 method=["GET"]))
479 method=["GET"]))
476
480
477 rmap.connect('pullrequest',
481 rmap.connect('pullrequest',
478 '/{repo_name:.*?}/pull-request/new', controller='pullrequests',
482 '/{repo_name:.*?}/pull-request/new', controller='pullrequests',
479 action='create', conditions=dict(function=check_repo,
483 action='create', conditions=dict(function=check_repo,
480 method=["POST"]))
484 method=["POST"]))
481
485
482 rmap.connect('pullrequest_show',
486 rmap.connect('pullrequest_show',
483 '/{repo_name:.*?}/pull-request/{pull_request_id}',
487 '/{repo_name:.*?}/pull-request/{pull_request_id}',
484 controller='pullrequests',
488 controller='pullrequests',
485 action='show', conditions=dict(function=check_repo,
489 action='show', conditions=dict(function=check_repo,
486 method=["GET"]))
490 method=["GET"]))
487 rmap.connect('pullrequest_update',
491 rmap.connect('pullrequest_update',
488 '/{repo_name:.*?}/pull-request/{pull_request_id}',
492 '/{repo_name:.*?}/pull-request/{pull_request_id}',
489 controller='pullrequests',
493 controller='pullrequests',
490 action='update', conditions=dict(function=check_repo,
494 action='update', conditions=dict(function=check_repo,
491 method=["PUT"]))
495 method=["PUT"]))
492 rmap.connect('pullrequest_delete',
496 rmap.connect('pullrequest_delete',
493 '/{repo_name:.*?}/pull-request/{pull_request_id}',
497 '/{repo_name:.*?}/pull-request/{pull_request_id}',
494 controller='pullrequests',
498 controller='pullrequests',
495 action='delete', conditions=dict(function=check_repo,
499 action='delete', conditions=dict(function=check_repo,
496 method=["DELETE"]))
500 method=["DELETE"]))
497
501
498 rmap.connect('pullrequest_show_all',
502 rmap.connect('pullrequest_show_all',
499 '/{repo_name:.*?}/pull-request',
503 '/{repo_name:.*?}/pull-request',
500 controller='pullrequests',
504 controller='pullrequests',
501 action='show_all', conditions=dict(function=check_repo,
505 action='show_all', conditions=dict(function=check_repo,
502 method=["GET"]))
506 method=["GET"]))
503
507
504 rmap.connect('pullrequest_comment',
508 rmap.connect('pullrequest_comment',
505 '/{repo_name:.*?}/pull-request-comment/{pull_request_id}',
509 '/{repo_name:.*?}/pull-request-comment/{pull_request_id}',
506 controller='pullrequests',
510 controller='pullrequests',
507 action='comment', conditions=dict(function=check_repo,
511 action='comment', conditions=dict(function=check_repo,
508 method=["POST"]))
512 method=["POST"]))
509
513
510 rmap.connect('pullrequest_comment_delete',
514 rmap.connect('pullrequest_comment_delete',
511 '/{repo_name:.*?}/pull-request-comment/{comment_id}/delete',
515 '/{repo_name:.*?}/pull-request-comment/{comment_id}/delete',
512 controller='pullrequests', action='delete_comment',
516 controller='pullrequests', action='delete_comment',
513 conditions=dict(function=check_repo, method=["DELETE"]))
517 conditions=dict(function=check_repo, method=["DELETE"]))
514
518
515 rmap.connect('summary_home', '/{repo_name:.*?}/summary',
519 rmap.connect('summary_home', '/{repo_name:.*?}/summary',
516 controller='summary', conditions=dict(function=check_repo))
520 controller='summary', conditions=dict(function=check_repo))
517
521
518 rmap.connect('shortlog_home', '/{repo_name:.*?}/shortlog',
522 rmap.connect('shortlog_home', '/{repo_name:.*?}/shortlog',
519 controller='shortlog', conditions=dict(function=check_repo))
523 controller='shortlog', conditions=dict(function=check_repo))
520
524
521 rmap.connect('shortlog_file_home', '/{repo_name:.*?}/shortlog/{revision}/{f_path:.*}',
525 rmap.connect('shortlog_file_home', '/{repo_name:.*?}/shortlog/{revision}/{f_path:.*}',
522 controller='shortlog', f_path=None,
526 controller='shortlog', f_path=None,
523 conditions=dict(function=check_repo))
527 conditions=dict(function=check_repo))
524
528
525 rmap.connect('branches_home', '/{repo_name:.*?}/branches',
529 rmap.connect('branches_home', '/{repo_name:.*?}/branches',
526 controller='branches', conditions=dict(function=check_repo))
530 controller='branches', conditions=dict(function=check_repo))
527
531
528 rmap.connect('tags_home', '/{repo_name:.*?}/tags',
532 rmap.connect('tags_home', '/{repo_name:.*?}/tags',
529 controller='tags', conditions=dict(function=check_repo))
533 controller='tags', conditions=dict(function=check_repo))
530
534
531 rmap.connect('bookmarks_home', '/{repo_name:.*?}/bookmarks',
535 rmap.connect('bookmarks_home', '/{repo_name:.*?}/bookmarks',
532 controller='bookmarks', conditions=dict(function=check_repo))
536 controller='bookmarks', conditions=dict(function=check_repo))
533
537
534 rmap.connect('changelog_home', '/{repo_name:.*?}/changelog',
538 rmap.connect('changelog_home', '/{repo_name:.*?}/changelog',
535 controller='changelog', conditions=dict(function=check_repo))
539 controller='changelog', conditions=dict(function=check_repo))
536
540
537 rmap.connect('changelog_details', '/{repo_name:.*?}/changelog_details/{cs}',
541 rmap.connect('changelog_details', '/{repo_name:.*?}/changelog_details/{cs}',
538 controller='changelog', action='changelog_details',
542 controller='changelog', action='changelog_details',
539 conditions=dict(function=check_repo))
543 conditions=dict(function=check_repo))
540
544
541 rmap.connect('files_home', '/{repo_name:.*?}/files/{revision}/{f_path:.*}',
545 rmap.connect('files_home', '/{repo_name:.*?}/files/{revision}/{f_path:.*}',
542 controller='files', revision='tip', f_path='',
546 controller='files', revision='tip', f_path='',
543 conditions=dict(function=check_repo))
547 conditions=dict(function=check_repo))
544
548
545 rmap.connect('files_history_home',
549 rmap.connect('files_history_home',
546 '/{repo_name:.*?}/history/{revision}/{f_path:.*}',
550 '/{repo_name:.*?}/history/{revision}/{f_path:.*}',
547 controller='files', action='history', revision='tip', f_path='',
551 controller='files', action='history', revision='tip', f_path='',
548 conditions=dict(function=check_repo))
552 conditions=dict(function=check_repo))
549
553
550 rmap.connect('files_diff_home', '/{repo_name:.*?}/diff/{f_path:.*}',
554 rmap.connect('files_diff_home', '/{repo_name:.*?}/diff/{f_path:.*}',
551 controller='files', action='diff', revision='tip', f_path='',
555 controller='files', action='diff', revision='tip', f_path='',
552 conditions=dict(function=check_repo))
556 conditions=dict(function=check_repo))
553
557
554 rmap.connect('files_rawfile_home',
558 rmap.connect('files_rawfile_home',
555 '/{repo_name:.*?}/rawfile/{revision}/{f_path:.*}',
559 '/{repo_name:.*?}/rawfile/{revision}/{f_path:.*}',
556 controller='files', action='rawfile', revision='tip',
560 controller='files', action='rawfile', revision='tip',
557 f_path='', conditions=dict(function=check_repo))
561 f_path='', conditions=dict(function=check_repo))
558
562
559 rmap.connect('files_raw_home',
563 rmap.connect('files_raw_home',
560 '/{repo_name:.*?}/raw/{revision}/{f_path:.*}',
564 '/{repo_name:.*?}/raw/{revision}/{f_path:.*}',
561 controller='files', action='raw', revision='tip', f_path='',
565 controller='files', action='raw', revision='tip', f_path='',
562 conditions=dict(function=check_repo))
566 conditions=dict(function=check_repo))
563
567
564 rmap.connect('files_annotate_home',
568 rmap.connect('files_annotate_home',
565 '/{repo_name:.*?}/annotate/{revision}/{f_path:.*}',
569 '/{repo_name:.*?}/annotate/{revision}/{f_path:.*}',
566 controller='files', action='index', revision='tip',
570 controller='files', action='index', revision='tip',
567 f_path='', annotate=True, conditions=dict(function=check_repo))
571 f_path='', annotate=True, conditions=dict(function=check_repo))
568
572
569 rmap.connect('files_edit_home',
573 rmap.connect('files_edit_home',
570 '/{repo_name:.*?}/edit/{revision}/{f_path:.*}',
574 '/{repo_name:.*?}/edit/{revision}/{f_path:.*}',
571 controller='files', action='edit', revision='tip',
575 controller='files', action='edit', revision='tip',
572 f_path='', conditions=dict(function=check_repo))
576 f_path='', conditions=dict(function=check_repo))
573
577
574 rmap.connect('files_add_home',
578 rmap.connect('files_add_home',
575 '/{repo_name:.*?}/add/{revision}/{f_path:.*}',
579 '/{repo_name:.*?}/add/{revision}/{f_path:.*}',
576 controller='files', action='add', revision='tip',
580 controller='files', action='add', revision='tip',
577 f_path='', conditions=dict(function=check_repo))
581 f_path='', conditions=dict(function=check_repo))
578
582
579 rmap.connect('files_archive_home', '/{repo_name:.*?}/archive/{fname}',
583 rmap.connect('files_archive_home', '/{repo_name:.*?}/archive/{fname}',
580 controller='files', action='archivefile',
584 controller='files', action='archivefile',
581 conditions=dict(function=check_repo))
585 conditions=dict(function=check_repo))
582
586
583 rmap.connect('files_nodelist_home',
587 rmap.connect('files_nodelist_home',
584 '/{repo_name:.*?}/nodelist/{revision}/{f_path:.*}',
588 '/{repo_name:.*?}/nodelist/{revision}/{f_path:.*}',
585 controller='files', action='nodelist',
589 controller='files', action='nodelist',
586 conditions=dict(function=check_repo))
590 conditions=dict(function=check_repo))
587
591
588 rmap.connect('repo_settings_delete', '/{repo_name:.*?}/settings',
592 rmap.connect('repo_settings_delete', '/{repo_name:.*?}/settings',
589 controller='settings', action="delete",
593 controller='settings', action="delete",
590 conditions=dict(method=["DELETE"], function=check_repo))
594 conditions=dict(method=["DELETE"], function=check_repo))
591
595
592 rmap.connect('repo_settings_update', '/{repo_name:.*?}/settings',
596 rmap.connect('repo_settings_update', '/{repo_name:.*?}/settings',
593 controller='settings', action="update",
597 controller='settings', action="update",
594 conditions=dict(method=["PUT"], function=check_repo))
598 conditions=dict(method=["PUT"], function=check_repo))
595
599
596 rmap.connect('repo_settings_home', '/{repo_name:.*?}/settings',
600 rmap.connect('repo_settings_home', '/{repo_name:.*?}/settings',
597 controller='settings', action='index',
601 controller='settings', action='index',
598 conditions=dict(function=check_repo))
602 conditions=dict(function=check_repo))
599
603
600 rmap.connect('toggle_locking', "/{repo_name:.*?}/locking_toggle",
604 rmap.connect('toggle_locking', "/{repo_name:.*?}/locking_toggle",
601 controller='settings', action="toggle_locking",
605 controller='settings', action="toggle_locking",
602 conditions=dict(method=["GET"], function=check_repo))
606 conditions=dict(method=["GET"], function=check_repo))
603
607
604 rmap.connect('repo_fork_create_home', '/{repo_name:.*?}/fork',
608 rmap.connect('repo_fork_create_home', '/{repo_name:.*?}/fork',
605 controller='forks', action='fork_create',
609 controller='forks', action='fork_create',
606 conditions=dict(function=check_repo, method=["POST"]))
610 conditions=dict(function=check_repo, method=["POST"]))
607
611
608 rmap.connect('repo_fork_home', '/{repo_name:.*?}/fork',
612 rmap.connect('repo_fork_home', '/{repo_name:.*?}/fork',
609 controller='forks', action='fork',
613 controller='forks', action='fork',
610 conditions=dict(function=check_repo))
614 conditions=dict(function=check_repo))
611
615
612 rmap.connect('repo_forks_home', '/{repo_name:.*?}/forks',
616 rmap.connect('repo_forks_home', '/{repo_name:.*?}/forks',
613 controller='forks', action='forks',
617 controller='forks', action='forks',
614 conditions=dict(function=check_repo))
618 conditions=dict(function=check_repo))
615
619
616 rmap.connect('repo_followers_home', '/{repo_name:.*?}/followers',
620 rmap.connect('repo_followers_home', '/{repo_name:.*?}/followers',
617 controller='followers', action='followers',
621 controller='followers', action='followers',
618 conditions=dict(function=check_repo))
622 conditions=dict(function=check_repo))
619
623
620 return rmap
624 return rmap
@@ -1,510 +1,511 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 RhodeCodeSetting
46 from rhodecode.model.forms import RepoForm
47 from rhodecode.model.forms import RepoForm
47 from rhodecode.model.scm import ScmModel
48 from rhodecode.model.scm import ScmModel
48 from rhodecode.model.repo import RepoModel
49 from rhodecode.model.repo import RepoModel
49 from rhodecode.lib.compat import json
50 from rhodecode.lib.compat import json
50 from sqlalchemy.sql.expression import func
51 from sqlalchemy.sql.expression import func
51
52
52 log = logging.getLogger(__name__)
53 log = logging.getLogger(__name__)
53
54
54
55
55 class ReposController(BaseController):
56 class ReposController(BaseController):
56 """
57 """
57 REST Controller styled on the Atom Publishing Protocol"""
58 REST Controller styled on the Atom Publishing Protocol"""
58 # To properly map this controller, ensure your config/routing.py
59 # To properly map this controller, ensure your config/routing.py
59 # file has a resource setup:
60 # file has a resource setup:
60 # map.resource('repo', 'repos')
61 # map.resource('repo', 'repos')
61
62
62 @LoginRequired()
63 @LoginRequired()
63 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
64 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
64 def __before__(self):
65 def __before__(self):
65 c.admin_user = session.get('admin_user')
66 c.admin_user = session.get('admin_user')
66 c.admin_username = session.get('admin_username')
67 c.admin_username = session.get('admin_username')
67 super(ReposController, self).__before__()
68 super(ReposController, self).__before__()
68
69
69 def __load_defaults(self):
70 def __load_defaults(self):
70 c.repo_groups = RepoGroup.groups_choices(check_perms=True)
71 c.repo_groups = RepoGroup.groups_choices(check_perms=True)
71 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
72 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
72
73
73 repo_model = RepoModel()
74 repo_model = RepoModel()
74 c.users_array = repo_model.get_users_js()
75 c.users_array = repo_model.get_users_js()
75 c.users_groups_array = repo_model.get_users_groups_js()
76 c.users_groups_array = repo_model.get_users_groups_js()
76 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
77 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
77 c.landing_revs_choices = choices
78 c.landing_revs_choices = choices
78
79
79 def __load_data(self, repo_name=None):
80 def __load_data(self, repo_name=None):
80 """
81 """
81 Load defaults settings for edit, and update
82 Load defaults settings for edit, and update
82
83
83 :param repo_name:
84 :param repo_name:
84 """
85 """
85 self.__load_defaults()
86 self.__load_defaults()
86
87
87 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
88 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
88 repo = db_repo.scm_instance
89 repo = db_repo.scm_instance
89
90
90 if c.repo_info is None:
91 if c.repo_info is None:
91 h.flash(_('%s repository is not mapped to db perhaps'
92 h.flash(_('%s repository is not mapped to db perhaps'
92 ' it was created or renamed from the filesystem'
93 ' it was created or renamed from the filesystem'
93 ' please run the application again'
94 ' please run the application again'
94 ' in order to rescan repositories') % repo_name,
95 ' in order to rescan repositories') % repo_name,
95 category='error')
96 category='error')
96
97
97 return redirect(url('repos'))
98 return redirect(url('repos'))
98
99
99 choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info)
100 choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info)
100 c.landing_revs_choices = choices
101 c.landing_revs_choices = choices
101
102
102 c.default_user_id = User.get_by_username('default').user_id
103 c.default_user_id = User.get_by_username('default').user_id
103 c.in_public_journal = UserFollowing.query()\
104 c.in_public_journal = UserFollowing.query()\
104 .filter(UserFollowing.user_id == c.default_user_id)\
105 .filter(UserFollowing.user_id == c.default_user_id)\
105 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
106 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
106
107
107 if c.repo_info.stats:
108 if c.repo_info.stats:
108 # this is on what revision we ended up so we add +1 for count
109 # this is on what revision we ended up so we add +1 for count
109 last_rev = c.repo_info.stats.stat_on_revision + 1
110 last_rev = c.repo_info.stats.stat_on_revision + 1
110 else:
111 else:
111 last_rev = 0
112 last_rev = 0
112 c.stats_revision = last_rev
113 c.stats_revision = last_rev
113
114
114 c.repo_last_rev = repo.count() if repo.revisions else 0
115 c.repo_last_rev = repo.count() if repo.revisions else 0
115
116
116 if last_rev == 0 or c.repo_last_rev == 0:
117 if last_rev == 0 or c.repo_last_rev == 0:
117 c.stats_percentage = 0
118 c.stats_percentage = 0
118 else:
119 else:
119 c.stats_percentage = '%.2f' % ((float((last_rev)) /
120 c.stats_percentage = '%.2f' % ((float((last_rev)) /
120 c.repo_last_rev) * 100)
121 c.repo_last_rev) * 100)
121
122
122 defaults = RepoModel()._get_defaults(repo_name)
123 defaults = RepoModel()._get_defaults(repo_name)
123
124
124 c.repos_list = [('', _('--REMOVE FORK--'))]
125 c.repos_list = [('', _('--REMOVE FORK--'))]
125 c.repos_list += [(x.repo_id, x.repo_name) for x in
126 c.repos_list += [(x.repo_id, x.repo_name) for x in
126 Repository.query().order_by(Repository.repo_name).all()
127 Repository.query().order_by(Repository.repo_name).all()
127 if x.repo_id != c.repo_info.repo_id]
128 if x.repo_id != c.repo_info.repo_id]
128
129
129 defaults['id_fork_of'] = db_repo.fork.repo_id if db_repo.fork else ''
130 defaults['id_fork_of'] = db_repo.fork.repo_id if db_repo.fork else ''
130 return defaults
131 return defaults
131
132
132 @HasPermissionAllDecorator('hg.admin')
133 @HasPermissionAllDecorator('hg.admin')
133 def index(self, format='html'):
134 def index(self, format='html'):
134 """GET /repos: All items in the collection"""
135 """GET /repos: All items in the collection"""
135 # url('repos')
136 # url('repos')
136
137
137 c.repos_list = Repository.query()\
138 c.repos_list = Repository.query()\
138 .order_by(func.lower(Repository.repo_name))\
139 .order_by(func.lower(Repository.repo_name))\
139 .all()
140 .all()
140
141
141 repos_data = []
142 repos_data = []
142 total_records = len(c.repos_list)
143 total_records = len(c.repos_list)
143
144
144 _tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
145 _tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
145 template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
146 template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
146
147
147 quick_menu = lambda repo_name: (template.get_def("quick_menu")
148 quick_menu = lambda repo_name: (template.get_def("quick_menu")
148 .render(repo_name, _=_, h=h, c=c))
149 .render(repo_name, _=_, h=h, c=c))
149 repo_lnk = lambda name, rtype, private, fork_of: (
150 repo_lnk = lambda name, rtype, private, fork_of: (
150 template.get_def("repo_name")
151 template.get_def("repo_name")
151 .render(name, rtype, private, fork_of, short_name=False,
152 .render(name, rtype, private, fork_of, short_name=False,
152 admin=True, _=_, h=h, c=c))
153 admin=True, _=_, h=h, c=c))
153
154
154 repo_actions = lambda repo_name: (template.get_def("repo_actions")
155 repo_actions = lambda repo_name: (template.get_def("repo_actions")
155 .render(repo_name, _=_, h=h, c=c))
156 .render(repo_name, _=_, h=h, c=c))
156
157
157 for repo in c.repos_list:
158 for repo in c.repos_list:
158 repos_data.append({
159 repos_data.append({
159 "menu": quick_menu(repo.repo_name),
160 "menu": quick_menu(repo.repo_name),
160 "raw_name": repo.repo_name.lower(),
161 "raw_name": repo.repo_name.lower(),
161 "name": repo_lnk(repo.repo_name, repo.repo_type,
162 "name": repo_lnk(repo.repo_name, repo.repo_type,
162 repo.private, repo.fork),
163 repo.private, repo.fork),
163 "desc": repo.description,
164 "desc": repo.description,
164 "owner": repo.user.username,
165 "owner": repo.user.username,
165 "action": repo_actions(repo.repo_name),
166 "action": repo_actions(repo.repo_name),
166 })
167 })
167
168
168 c.data = json.dumps({
169 c.data = json.dumps({
169 "totalRecords": total_records,
170 "totalRecords": total_records,
170 "startIndex": 0,
171 "startIndex": 0,
171 "sort": "name",
172 "sort": "name",
172 "dir": "asc",
173 "dir": "asc",
173 "records": repos_data
174 "records": repos_data
174 })
175 })
175
176
176 return render('admin/repos/repos.html')
177 return render('admin/repos/repos.html')
177
178
178 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
179 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
179 def create(self):
180 def create(self):
180 """
181 """
181 POST /repos: Create a new item"""
182 POST /repos: Create a new item"""
182 # url('repos')
183 # url('repos')
183
184
184 self.__load_defaults()
185 self.__load_defaults()
185 form_result = {}
186 form_result = {}
186 try:
187 try:
187 form_result = RepoForm(repo_groups=c.repo_groups_choices,
188 form_result = RepoForm(repo_groups=c.repo_groups_choices,
188 landing_revs=c.landing_revs_choices)()\
189 landing_revs=c.landing_revs_choices)()\
189 .to_python(dict(request.POST))
190 .to_python(dict(request.POST))
190 new_repo = RepoModel().create(form_result,
191 new_repo = RepoModel().create(form_result,
191 self.rhodecode_user.user_id)
192 self.rhodecode_user.user_id)
192 if form_result['clone_uri']:
193 if form_result['clone_uri']:
193 h.flash(_('created repository %s from %s') \
194 h.flash(_('created repository %s from %s') \
194 % (form_result['repo_name'], form_result['clone_uri']),
195 % (form_result['repo_name'], form_result['clone_uri']),
195 category='success')
196 category='success')
196 else:
197 else:
197 h.flash(_('created repository %s') % form_result['repo_name'],
198 h.flash(_('created repository %s') % form_result['repo_name'],
198 category='success')
199 category='success')
199
200
200 if request.POST.get('user_created'):
201 if request.POST.get('user_created'):
201 # created by regular non admin user
202 # created by regular non admin user
202 action_logger(self.rhodecode_user, 'user_created_repo',
203 action_logger(self.rhodecode_user, 'user_created_repo',
203 form_result['repo_name_full'], self.ip_addr,
204 form_result['repo_name_full'], self.ip_addr,
204 self.sa)
205 self.sa)
205 else:
206 else:
206 action_logger(self.rhodecode_user, 'admin_created_repo',
207 action_logger(self.rhodecode_user, 'admin_created_repo',
207 form_result['repo_name_full'], self.ip_addr,
208 form_result['repo_name_full'], self.ip_addr,
208 self.sa)
209 self.sa)
209 Session().commit()
210 Session().commit()
210 except formencode.Invalid, errors:
211 except formencode.Invalid, errors:
211
212
212 c.new_repo = errors.value['repo_name']
213 c.new_repo = errors.value['repo_name']
213
214
214 if request.POST.get('user_created'):
215 if request.POST.get('user_created'):
215 r = render('admin/repos/repo_add_create_repository.html')
216 r = render('admin/repos/repo_add_create_repository.html')
216 else:
217 else:
217 r = render('admin/repos/repo_add.html')
218 r = render('admin/repos/repo_add.html')
218
219
219 return htmlfill.render(
220 return htmlfill.render(
220 r,
221 r,
221 defaults=errors.value,
222 defaults=errors.value,
222 errors=errors.error_dict or {},
223 errors=errors.error_dict or {},
223 prefix_error=False,
224 prefix_error=False,
224 encoding="UTF-8")
225 encoding="UTF-8")
225
226
226 except Exception:
227 except Exception:
227 log.error(traceback.format_exc())
228 log.error(traceback.format_exc())
228 msg = _('error occurred during creation of repository %s') \
229 msg = _('error occurred during creation of repository %s') \
229 % form_result.get('repo_name')
230 % form_result.get('repo_name')
230 h.flash(msg, category='error')
231 h.flash(msg, category='error')
231 return redirect(url('repos'))
232 return redirect(url('repos'))
232 #redirect to our new repo !
233 #redirect to our new repo !
233 return redirect(url('summary_home', repo_name=new_repo.repo_name))
234 return redirect(url('summary_home', repo_name=new_repo.repo_name))
234
235
235 @HasPermissionAllDecorator('hg.admin')
236 @HasPermissionAllDecorator('hg.admin')
236 def new(self, format='html'):
237 def new(self, format='html'):
237 """GET /repos/new: Form to create a new item"""
238 """GET /repos/new: Form to create a new item"""
238 new_repo = request.GET.get('repo', '')
239 new_repo = request.GET.get('repo', '')
239 c.new_repo = repo_name_slug(new_repo)
240 c.new_repo = repo_name_slug(new_repo)
240 self.__load_defaults()
241 self.__load_defaults()
241 return render('admin/repos/repo_add.html')
242 return render('admin/repos/repo_add.html')
242
243
243 @HasPermissionAllDecorator('hg.admin')
244 @HasPermissionAllDecorator('hg.admin')
244 def update(self, repo_name):
245 def update(self, repo_name):
245 """
246 """
246 PUT /repos/repo_name: Update an existing item"""
247 PUT /repos/repo_name: Update an existing item"""
247 # Forms posted to this method should contain a hidden field:
248 # Forms posted to this method should contain a hidden field:
248 # <input type="hidden" name="_method" value="PUT" />
249 # <input type="hidden" name="_method" value="PUT" />
249 # Or using helpers:
250 # Or using helpers:
250 # h.form(url('repo', repo_name=ID),
251 # h.form(url('repo', repo_name=ID),
251 # method='put')
252 # method='put')
252 # url('repo', repo_name=ID)
253 # url('repo', repo_name=ID)
253 self.__load_defaults()
254 self.__load_defaults()
254 repo_model = RepoModel()
255 repo_model = RepoModel()
255 changed_name = repo_name
256 changed_name = repo_name
256 #override the choices with extracted revisions !
257 #override the choices with extracted revisions !
257 choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo_name)
258 choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo_name)
258 c.landing_revs_choices = choices
259 c.landing_revs_choices = choices
259
260
260 _form = RepoForm(edit=True, old_data={'repo_name': repo_name},
261 _form = RepoForm(edit=True, old_data={'repo_name': repo_name},
261 repo_groups=c.repo_groups_choices,
262 repo_groups=c.repo_groups_choices,
262 landing_revs=c.landing_revs_choices)()
263 landing_revs=c.landing_revs_choices)()
263 try:
264 try:
264 form_result = _form.to_python(dict(request.POST))
265 form_result = _form.to_python(dict(request.POST))
265 repo = repo_model.update(repo_name, form_result)
266 repo = repo_model.update(repo_name, form_result)
266 invalidate_cache('get_repo_cached_%s' % repo_name)
267 invalidate_cache('get_repo_cached_%s' % repo_name)
267 h.flash(_('Repository %s updated successfully') % repo_name,
268 h.flash(_('Repository %s updated successfully') % repo_name,
268 category='success')
269 category='success')
269 changed_name = repo.repo_name
270 changed_name = repo.repo_name
270 action_logger(self.rhodecode_user, 'admin_updated_repo',
271 action_logger(self.rhodecode_user, 'admin_updated_repo',
271 changed_name, self.ip_addr, self.sa)
272 changed_name, self.ip_addr, self.sa)
272 Session().commit()
273 Session().commit()
273 except formencode.Invalid, errors:
274 except formencode.Invalid, errors:
274 defaults = self.__load_data(repo_name)
275 defaults = self.__load_data(repo_name)
275 defaults.update(errors.value)
276 defaults.update(errors.value)
276 return htmlfill.render(
277 return htmlfill.render(
277 render('admin/repos/repo_edit.html'),
278 render('admin/repos/repo_edit.html'),
278 defaults=defaults,
279 defaults=defaults,
279 errors=errors.error_dict or {},
280 errors=errors.error_dict or {},
280 prefix_error=False,
281 prefix_error=False,
281 encoding="UTF-8")
282 encoding="UTF-8")
282
283
283 except Exception:
284 except Exception:
284 log.error(traceback.format_exc())
285 log.error(traceback.format_exc())
285 h.flash(_('error occurred during update of repository %s') \
286 h.flash(_('error occurred during update of repository %s') \
286 % repo_name, category='error')
287 % repo_name, category='error')
287 return redirect(url('edit_repo', repo_name=changed_name))
288 return redirect(url('edit_repo', repo_name=changed_name))
288
289
289 @HasPermissionAllDecorator('hg.admin')
290 @HasPermissionAllDecorator('hg.admin')
290 def delete(self, repo_name):
291 def delete(self, repo_name):
291 """
292 """
292 DELETE /repos/repo_name: Delete an existing item"""
293 DELETE /repos/repo_name: Delete an existing item"""
293 # Forms posted to this method should contain a hidden field:
294 # Forms posted to this method should contain a hidden field:
294 # <input type="hidden" name="_method" value="DELETE" />
295 # <input type="hidden" name="_method" value="DELETE" />
295 # Or using helpers:
296 # Or using helpers:
296 # h.form(url('repo', repo_name=ID),
297 # h.form(url('repo', repo_name=ID),
297 # method='delete')
298 # method='delete')
298 # url('repo', repo_name=ID)
299 # url('repo', repo_name=ID)
299
300
300 repo_model = RepoModel()
301 repo_model = RepoModel()
301 repo = repo_model.get_by_repo_name(repo_name)
302 repo = repo_model.get_by_repo_name(repo_name)
302 if not repo:
303 if not repo:
303 h.flash(_('%s repository is not mapped to db perhaps'
304 h.flash(_('%s repository is not mapped to db perhaps'
304 ' it was moved or renamed from the filesystem'
305 ' it was moved or renamed from the filesystem'
305 ' please run the application again'
306 ' please run the application again'
306 ' in order to rescan repositories') % repo_name,
307 ' in order to rescan repositories') % repo_name,
307 category='error')
308 category='error')
308
309
309 return redirect(url('repos'))
310 return redirect(url('repos'))
310 try:
311 try:
311 action_logger(self.rhodecode_user, 'admin_deleted_repo',
312 action_logger(self.rhodecode_user, 'admin_deleted_repo',
312 repo_name, self.ip_addr, self.sa)
313 repo_name, self.ip_addr, self.sa)
313 repo_model.delete(repo)
314 repo_model.delete(repo)
314 invalidate_cache('get_repo_cached_%s' % repo_name)
315 invalidate_cache('get_repo_cached_%s' % repo_name)
315 h.flash(_('deleted repository %s') % repo_name, category='success')
316 h.flash(_('deleted repository %s') % repo_name, category='success')
316 Session().commit()
317 Session().commit()
317 except IntegrityError, e:
318 except IntegrityError, e:
318 if e.message.find('repositories_fork_id_fkey') != -1:
319 if e.message.find('repositories_fork_id_fkey') != -1:
319 log.error(traceback.format_exc())
320 log.error(traceback.format_exc())
320 h.flash(_('Cannot delete %s it still contains attached '
321 h.flash(_('Cannot delete %s it still contains attached '
321 'forks') % repo_name,
322 'forks') % repo_name,
322 category='warning')
323 category='warning')
323 else:
324 else:
324 log.error(traceback.format_exc())
325 log.error(traceback.format_exc())
325 h.flash(_('An error occurred during '
326 h.flash(_('An error occurred during '
326 'deletion of %s') % repo_name,
327 'deletion of %s') % repo_name,
327 category='error')
328 category='error')
328
329
329 except Exception, e:
330 except Exception, e:
330 log.error(traceback.format_exc())
331 log.error(traceback.format_exc())
331 h.flash(_('An error occurred during deletion of %s') % repo_name,
332 h.flash(_('An error occurred during deletion of %s') % repo_name,
332 category='error')
333 category='error')
333
334
334 return redirect(url('repos'))
335 return redirect(url('repos'))
335
336
336 @HasRepoPermissionAllDecorator('repository.admin')
337 @HasRepoPermissionAllDecorator('repository.admin')
337 def delete_perm_user(self, repo_name):
338 def delete_perm_user(self, repo_name):
338 """
339 """
339 DELETE an existing repository permission user
340 DELETE an existing repository permission user
340
341
341 :param repo_name:
342 :param repo_name:
342 """
343 """
343 try:
344 try:
344 RepoModel().revoke_user_permission(repo=repo_name,
345 RepoModel().revoke_user_permission(repo=repo_name,
345 user=request.POST['user_id'])
346 user=request.POST['user_id'])
346 Session().commit()
347 Session().commit()
347 except Exception:
348 except Exception:
348 log.error(traceback.format_exc())
349 log.error(traceback.format_exc())
349 h.flash(_('An error occurred during deletion of repository user'),
350 h.flash(_('An error occurred during deletion of repository user'),
350 category='error')
351 category='error')
351 raise HTTPInternalServerError()
352 raise HTTPInternalServerError()
352
353
353 @HasRepoPermissionAllDecorator('repository.admin')
354 @HasRepoPermissionAllDecorator('repository.admin')
354 def delete_perm_users_group(self, repo_name):
355 def delete_perm_users_group(self, repo_name):
355 """
356 """
356 DELETE an existing repository permission users group
357 DELETE an existing repository permission users group
357
358
358 :param repo_name:
359 :param repo_name:
359 """
360 """
360
361
361 try:
362 try:
362 RepoModel().revoke_users_group_permission(
363 RepoModel().revoke_users_group_permission(
363 repo=repo_name, group_name=request.POST['users_group_id']
364 repo=repo_name, group_name=request.POST['users_group_id']
364 )
365 )
365 Session().commit()
366 Session().commit()
366 except Exception:
367 except Exception:
367 log.error(traceback.format_exc())
368 log.error(traceback.format_exc())
368 h.flash(_('An error occurred during deletion of repository'
369 h.flash(_('An error occurred during deletion of repository'
369 ' users groups'),
370 ' users groups'),
370 category='error')
371 category='error')
371 raise HTTPInternalServerError()
372 raise HTTPInternalServerError()
372
373
373 @HasPermissionAllDecorator('hg.admin')
374 @HasPermissionAllDecorator('hg.admin')
374 def repo_stats(self, repo_name):
375 def repo_stats(self, repo_name):
375 """
376 """
376 DELETE an existing repository statistics
377 DELETE an existing repository statistics
377
378
378 :param repo_name:
379 :param repo_name:
379 """
380 """
380
381
381 try:
382 try:
382 RepoModel().delete_stats(repo_name)
383 RepoModel().delete_stats(repo_name)
383 Session().commit()
384 Session().commit()
384 except Exception, e:
385 except Exception, e:
385 log.error(traceback.format_exc())
386 log.error(traceback.format_exc())
386 h.flash(_('An error occurred during deletion of repository stats'),
387 h.flash(_('An error occurred during deletion of repository stats'),
387 category='error')
388 category='error')
388 return redirect(url('edit_repo', repo_name=repo_name))
389 return redirect(url('edit_repo', repo_name=repo_name))
389
390
390 @HasPermissionAllDecorator('hg.admin')
391 @HasPermissionAllDecorator('hg.admin')
391 def repo_cache(self, repo_name):
392 def repo_cache(self, repo_name):
392 """
393 """
393 INVALIDATE existing repository cache
394 INVALIDATE existing repository cache
394
395
395 :param repo_name:
396 :param repo_name:
396 """
397 """
397
398
398 try:
399 try:
399 ScmModel().mark_for_invalidation(repo_name)
400 ScmModel().mark_for_invalidation(repo_name)
400 Session().commit()
401 Session().commit()
401 except Exception, e:
402 except Exception, e:
402 log.error(traceback.format_exc())
403 log.error(traceback.format_exc())
403 h.flash(_('An error occurred during cache invalidation'),
404 h.flash(_('An error occurred during cache invalidation'),
404 category='error')
405 category='error')
405 return redirect(url('edit_repo', repo_name=repo_name))
406 return redirect(url('edit_repo', repo_name=repo_name))
406
407
407 @HasPermissionAllDecorator('hg.admin')
408 @HasPermissionAllDecorator('hg.admin')
408 def repo_locking(self, repo_name):
409 def repo_locking(self, repo_name):
409 """
410 """
410 Unlock repository when it is locked !
411 Unlock repository when it is locked !
411
412
412 :param repo_name:
413 :param repo_name:
413 """
414 """
414
415
415 try:
416 try:
416 repo = Repository.get_by_repo_name(repo_name)
417 repo = Repository.get_by_repo_name(repo_name)
417 if request.POST.get('set_lock'):
418 if request.POST.get('set_lock'):
418 Repository.lock(repo, c.rhodecode_user.user_id)
419 Repository.lock(repo, c.rhodecode_user.user_id)
419 elif request.POST.get('set_unlock'):
420 elif request.POST.get('set_unlock'):
420 Repository.unlock(repo)
421 Repository.unlock(repo)
421 except Exception, e:
422 except Exception, e:
422 log.error(traceback.format_exc())
423 log.error(traceback.format_exc())
423 h.flash(_('An error occurred during unlocking'),
424 h.flash(_('An error occurred during unlocking'),
424 category='error')
425 category='error')
425 return redirect(url('edit_repo', repo_name=repo_name))
426 return redirect(url('edit_repo', repo_name=repo_name))
426
427
427 @HasPermissionAllDecorator('hg.admin')
428 @HasPermissionAllDecorator('hg.admin')
428 def repo_public_journal(self, repo_name):
429 def repo_public_journal(self, repo_name):
429 """
430 """
430 Set's this repository to be visible in public journal,
431 Set's this repository to be visible in public journal,
431 in other words assing default user to follow this repo
432 in other words assing default user to follow this repo
432
433
433 :param repo_name:
434 :param repo_name:
434 """
435 """
435
436
436 cur_token = request.POST.get('auth_token')
437 cur_token = request.POST.get('auth_token')
437 token = get_token()
438 token = get_token()
438 if cur_token == token:
439 if cur_token == token:
439 try:
440 try:
440 repo_id = Repository.get_by_repo_name(repo_name).repo_id
441 repo_id = Repository.get_by_repo_name(repo_name).repo_id
441 user_id = User.get_by_username('default').user_id
442 user_id = User.get_by_username('default').user_id
442 self.scm_model.toggle_following_repo(repo_id, user_id)
443 self.scm_model.toggle_following_repo(repo_id, user_id)
443 h.flash(_('Updated repository visibility in public journal'),
444 h.flash(_('Updated repository visibility in public journal'),
444 category='success')
445 category='success')
445 Session().commit()
446 Session().commit()
446 except:
447 except:
447 h.flash(_('An error occurred during setting this'
448 h.flash(_('An error occurred during setting this'
448 ' repository in public journal'),
449 ' repository in public journal'),
449 category='error')
450 category='error')
450
451
451 else:
452 else:
452 h.flash(_('Token mismatch'), category='error')
453 h.flash(_('Token mismatch'), category='error')
453 return redirect(url('edit_repo', repo_name=repo_name))
454 return redirect(url('edit_repo', repo_name=repo_name))
454
455
455 @HasPermissionAllDecorator('hg.admin')
456 @HasPermissionAllDecorator('hg.admin')
456 def repo_pull(self, repo_name):
457 def repo_pull(self, repo_name):
457 """
458 """
458 Runs task to update given repository with remote changes,
459 Runs task to update given repository with remote changes,
459 ie. make pull on remote location
460 ie. make pull on remote location
460
461
461 :param repo_name:
462 :param repo_name:
462 """
463 """
463 try:
464 try:
464 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
465 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
465 h.flash(_('Pulled from remote location'), category='success')
466 h.flash(_('Pulled from remote location'), category='success')
466 except Exception, e:
467 except Exception, e:
467 h.flash(_('An error occurred during pull from remote location'),
468 h.flash(_('An error occurred during pull from remote location'),
468 category='error')
469 category='error')
469
470
470 return redirect(url('edit_repo', repo_name=repo_name))
471 return redirect(url('edit_repo', repo_name=repo_name))
471
472
472 @HasPermissionAllDecorator('hg.admin')
473 @HasPermissionAllDecorator('hg.admin')
473 def repo_as_fork(self, repo_name):
474 def repo_as_fork(self, repo_name):
474 """
475 """
475 Mark given repository as a fork of another
476 Mark given repository as a fork of another
476
477
477 :param repo_name:
478 :param repo_name:
478 """
479 """
479 try:
480 try:
480 fork_id = request.POST.get('id_fork_of')
481 fork_id = request.POST.get('id_fork_of')
481 repo = ScmModel().mark_as_fork(repo_name, fork_id,
482 repo = ScmModel().mark_as_fork(repo_name, fork_id,
482 self.rhodecode_user.username)
483 self.rhodecode_user.username)
483 fork = repo.fork.repo_name if repo.fork else _('Nothing')
484 fork = repo.fork.repo_name if repo.fork else _('Nothing')
484 Session().commit()
485 Session().commit()
485 h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork),
486 h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork),
486 category='success')
487 category='success')
487 except Exception, e:
488 except Exception, e:
488 log.error(traceback.format_exc())
489 log.error(traceback.format_exc())
489 h.flash(_('An error occurred during this operation'),
490 h.flash(_('An error occurred during this operation'),
490 category='error')
491 category='error')
491
492
492 return redirect(url('edit_repo', repo_name=repo_name))
493 return redirect(url('edit_repo', repo_name=repo_name))
493
494
494 @HasPermissionAllDecorator('hg.admin')
495 @HasPermissionAllDecorator('hg.admin')
495 def show(self, repo_name, format='html'):
496 def show(self, repo_name, format='html'):
496 """GET /repos/repo_name: Show a specific item"""
497 """GET /repos/repo_name: Show a specific item"""
497 # url('repo', repo_name=ID)
498 # url('repo', repo_name=ID)
498
499
499 @HasPermissionAllDecorator('hg.admin')
500 @HasPermissionAllDecorator('hg.admin')
500 def edit(self, repo_name, format='html'):
501 def edit(self, repo_name, format='html'):
501 """GET /repos/repo_name/edit: Form to edit an existing item"""
502 """GET /repos/repo_name/edit: Form to edit an existing item"""
502 # url('edit_repo', repo_name=ID)
503 # url('edit_repo', repo_name=ID)
503 defaults = self.__load_data(repo_name)
504 defaults = self.__load_data(repo_name)
504
505
505 return htmlfill.render(
506 return htmlfill.render(
506 render('admin/repos/repo_edit.html'),
507 render('admin/repos/repo_edit.html'),
507 defaults=defaults,
508 defaults=defaults,
508 encoding="UTF-8",
509 encoding="UTF-8",
509 force_defaults=False
510 force_defaults=False
510 )
511 )
@@ -1,508 +1,516 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, check_git_version
44 set_rhodecode_config, repo_name_slug, check_git_version
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, ApplicationVisualisationForm
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 from rhodecode.lib.utils2 import str2bool
54 from rhodecode.lib.utils2 import str2bool
55
55
56 log = logging.getLogger(__name__)
56 log = logging.getLogger(__name__)
57
57
58
58
59 class SettingsController(BaseController):
59 class SettingsController(BaseController):
60 """REST Controller styled on the Atom Publishing Protocol"""
60 """REST Controller styled on the Atom Publishing Protocol"""
61 # To properly map this controller, ensure your config/routing.py
61 # To properly map this controller, ensure your config/routing.py
62 # file has a resource setup:
62 # file has a resource setup:
63 # map.resource('setting', 'settings', controller='admin/settings',
63 # map.resource('setting', 'settings', controller='admin/settings',
64 # path_prefix='/admin', name_prefix='admin_')
64 # path_prefix='/admin', name_prefix='admin_')
65
65
66 @LoginRequired()
66 @LoginRequired()
67 def __before__(self):
67 def __before__(self):
68 c.admin_user = session.get('admin_user')
68 c.admin_user = session.get('admin_user')
69 c.admin_username = session.get('admin_username')
69 c.admin_username = session.get('admin_username')
70 c.modules = sorted([(p.project_name, p.version)
70 c.modules = sorted([(p.project_name, p.version)
71 for p in pkg_resources.working_set]
71 for p in pkg_resources.working_set]
72 + [('git', check_git_version())],
72 + [('git', check_git_version())],
73 key=lambda k: k[0].lower())
73 key=lambda k: k[0].lower())
74 c.py_version = platform.python_version()
74 c.py_version = platform.python_version()
75 c.platform = platform.platform()
75 c.platform = platform.platform()
76 super(SettingsController, self).__before__()
76 super(SettingsController, self).__before__()
77
77
78 @HasPermissionAllDecorator('hg.admin')
78 @HasPermissionAllDecorator('hg.admin')
79 def index(self, format='html'):
79 def index(self, format='html'):
80 """GET /admin/settings: All items in the collection"""
80 """GET /admin/settings: All items in the collection"""
81 # url('admin_settings')
81 # url('admin_settings')
82
82
83 defaults = RhodeCodeSetting.get_app_settings()
83 defaults = RhodeCodeSetting.get_app_settings()
84 defaults.update(self._get_hg_ui_settings())
84 defaults.update(self._get_hg_ui_settings())
85
85
86 return htmlfill.render(
86 return htmlfill.render(
87 render('admin/settings/settings.html'),
87 render('admin/settings/settings.html'),
88 defaults=defaults,
88 defaults=defaults,
89 encoding="UTF-8",
89 encoding="UTF-8",
90 force_defaults=False
90 force_defaults=False
91 )
91 )
92
92
93 @HasPermissionAllDecorator('hg.admin')
93 @HasPermissionAllDecorator('hg.admin')
94 def create(self):
94 def create(self):
95 """POST /admin/settings: Create a new item"""
95 """POST /admin/settings: Create a new item"""
96 # url('admin_settings')
96 # url('admin_settings')
97
97
98 @HasPermissionAllDecorator('hg.admin')
98 @HasPermissionAllDecorator('hg.admin')
99 def new(self, format='html'):
99 def new(self, format='html'):
100 """GET /admin/settings/new: Form to create a new item"""
100 """GET /admin/settings/new: Form to create a new item"""
101 # url('admin_new_setting')
101 # url('admin_new_setting')
102
102
103 @HasPermissionAllDecorator('hg.admin')
103 @HasPermissionAllDecorator('hg.admin')
104 def update(self, setting_id):
104 def update(self, setting_id):
105 """PUT /admin/settings/setting_id: Update an existing item"""
105 """PUT /admin/settings/setting_id: Update an existing item"""
106 # Forms posted to this method should contain a hidden field:
106 # Forms posted to this method should contain a hidden field:
107 # <input type="hidden" name="_method" value="PUT" />
107 # <input type="hidden" name="_method" value="PUT" />
108 # Or using helpers:
108 # Or using helpers:
109 # h.form(url('admin_setting', setting_id=ID),
109 # h.form(url('admin_setting', setting_id=ID),
110 # method='put')
110 # method='put')
111 # url('admin_setting', setting_id=ID)
111 # url('admin_setting', setting_id=ID)
112
112
113 if setting_id == 'mapping':
113 if setting_id == 'mapping':
114 rm_obsolete = request.POST.get('destroy', False)
114 rm_obsolete = request.POST.get('destroy', False)
115 log.debug('Rescanning directories with destroy=%s' % rm_obsolete)
115 log.debug('Rescanning directories with destroy=%s' % rm_obsolete)
116 initial = ScmModel().repo_scan()
116 initial = ScmModel().repo_scan()
117 log.debug('invalidating all repositories')
117 log.debug('invalidating all repositories')
118 for repo_name in initial.keys():
118 for repo_name in initial.keys():
119 invalidate_cache('get_repo_cached_%s' % repo_name)
119 invalidate_cache('get_repo_cached_%s' % repo_name)
120
120
121 added, removed = repo2db_mapper(initial, rm_obsolete)
121 added, removed = repo2db_mapper(initial, rm_obsolete)
122
122
123 h.flash(_('Repositories successfully'
123 h.flash(_('Repositories successfully'
124 ' rescanned added: %s,removed: %s') % (added, removed),
124 ' rescanned added: %s,removed: %s') % (added, removed),
125 category='success')
125 category='success')
126
126
127 if setting_id == 'whoosh':
127 if setting_id == 'whoosh':
128 repo_location = self._get_hg_ui_settings()['paths_root_path']
128 repo_location = self._get_hg_ui_settings()['paths_root_path']
129 full_index = request.POST.get('full_index', False)
129 full_index = request.POST.get('full_index', False)
130 run_task(tasks.whoosh_index, repo_location, full_index)
130 run_task(tasks.whoosh_index, repo_location, full_index)
131 h.flash(_('Whoosh reindex task scheduled'), category='success')
131 h.flash(_('Whoosh reindex task scheduled'), category='success')
132
132
133 if setting_id == 'global':
133 if setting_id == 'global':
134
134
135 application_form = ApplicationSettingsForm()()
135 application_form = ApplicationSettingsForm()()
136 try:
136 try:
137 form_result = application_form.to_python(dict(request.POST))
137 form_result = application_form.to_python(dict(request.POST))
138 except formencode.Invalid, errors:
138 except formencode.Invalid, errors:
139 return htmlfill.render(
139 return htmlfill.render(
140 render('admin/settings/settings.html'),
140 render('admin/settings/settings.html'),
141 defaults=errors.value,
141 defaults=errors.value,
142 errors=errors.error_dict or {},
142 errors=errors.error_dict or {},
143 prefix_error=False,
143 prefix_error=False,
144 encoding="UTF-8"
144 encoding="UTF-8"
145 )
145 )
146
146
147 try:
147 try:
148 sett1 = RhodeCodeSetting.get_by_name_or_create('title')
148 sett1 = RhodeCodeSetting.get_by_name_or_create('title')
149 sett1.app_settings_value = form_result['rhodecode_title']
149 sett1.app_settings_value = form_result['rhodecode_title']
150 Session().add(sett1)
150 Session().add(sett1)
151
151
152 sett2 = RhodeCodeSetting.get_by_name_or_create('realm')
152 sett2 = RhodeCodeSetting.get_by_name_or_create('realm')
153 sett2.app_settings_value = form_result['rhodecode_realm']
153 sett2.app_settings_value = form_result['rhodecode_realm']
154 Session().add(sett2)
154 Session().add(sett2)
155
155
156 sett3 = RhodeCodeSetting.get_by_name_or_create('ga_code')
156 sett3 = RhodeCodeSetting.get_by_name_or_create('ga_code')
157 sett3.app_settings_value = form_result['rhodecode_ga_code']
157 sett3.app_settings_value = form_result['rhodecode_ga_code']
158 Session().add(sett3)
158 Session().add(sett3)
159
159
160 Session().commit()
160 Session().commit()
161 set_rhodecode_config(config)
161 set_rhodecode_config(config)
162 h.flash(_('Updated application settings'), category='success')
162 h.flash(_('Updated application settings'), category='success')
163
163
164 except Exception:
164 except Exception:
165 log.error(traceback.format_exc())
165 log.error(traceback.format_exc())
166 h.flash(_('error occurred during updating '
166 h.flash(_('error occurred during updating '
167 'application settings'),
167 'application settings'),
168 category='error')
168 category='error')
169
169
170 if setting_id == 'visual':
170 if setting_id == 'visual':
171
171
172 application_form = ApplicationVisualisationForm()()
172 application_form = ApplicationVisualisationForm()()
173 try:
173 try:
174 form_result = application_form.to_python(dict(request.POST))
174 form_result = application_form.to_python(dict(request.POST))
175 except formencode.Invalid, errors:
175 except formencode.Invalid, errors:
176 return htmlfill.render(
176 return htmlfill.render(
177 render('admin/settings/settings.html'),
177 render('admin/settings/settings.html'),
178 defaults=errors.value,
178 defaults=errors.value,
179 errors=errors.error_dict or {},
179 errors=errors.error_dict or {},
180 prefix_error=False,
180 prefix_error=False,
181 encoding="UTF-8"
181 encoding="UTF-8"
182 )
182 )
183
183
184 try:
184 try:
185 sett1 = RhodeCodeSetting.get_by_name_or_create('show_public_icon')
185 sett1 = RhodeCodeSetting.get_by_name_or_create('show_public_icon')
186 sett1.app_settings_value = \
186 sett1.app_settings_value = \
187 form_result['rhodecode_show_public_icon']
187 form_result['rhodecode_show_public_icon']
188 Session().add(sett1)
188 Session().add(sett1)
189
189
190 sett2 = RhodeCodeSetting.get_by_name_or_create('show_private_icon')
190 sett2 = RhodeCodeSetting.get_by_name_or_create('show_private_icon')
191 sett2.app_settings_value = \
191 sett2.app_settings_value = \
192 form_result['rhodecode_show_private_icon']
192 form_result['rhodecode_show_private_icon']
193 Session().add(sett2)
193 Session().add(sett2)
194
194
195 sett3 = RhodeCodeSetting.get_by_name_or_create('stylify_metatags')
195 sett3 = RhodeCodeSetting.get_by_name_or_create('stylify_metatags')
196 sett3.app_settings_value = \
196 sett3.app_settings_value = \
197 form_result['rhodecode_stylify_metatags']
197 form_result['rhodecode_stylify_metatags']
198 Session().add(sett3)
198 Session().add(sett3)
199
199
200 sett4 = RhodeCodeSetting.get_by_name_or_create('lightweight_dashboard')
200 sett4 = RhodeCodeSetting.get_by_name_or_create('lightweight_dashboard')
201 sett4.app_settings_value = \
201 sett4.app_settings_value = \
202 form_result['rhodecode_lightweight_dashboard']
202 form_result['rhodecode_lightweight_dashboard']
203 Session().add(sett4)
203 Session().add(sett4)
204
204
205 Session().commit()
205 Session().commit()
206 set_rhodecode_config(config)
206 set_rhodecode_config(config)
207 h.flash(_('Updated visualisation settings'),
207 h.flash(_('Updated visualisation settings'),
208 category='success')
208 category='success')
209
209
210 except Exception:
210 except Exception:
211 log.error(traceback.format_exc())
211 log.error(traceback.format_exc())
212 h.flash(_('error occurred during updating '
212 h.flash(_('error occurred during updating '
213 'visualisation settings'),
213 'visualisation settings'),
214 category='error')
214 category='error')
215
215
216 if setting_id == 'vcs':
216 if setting_id == 'vcs':
217 application_form = ApplicationUiSettingsForm()()
217 application_form = ApplicationUiSettingsForm()()
218 try:
218 try:
219 form_result = application_form.to_python(dict(request.POST))
219 form_result = application_form.to_python(dict(request.POST))
220 except formencode.Invalid, errors:
220 except formencode.Invalid, errors:
221 return htmlfill.render(
221 return htmlfill.render(
222 render('admin/settings/settings.html'),
222 render('admin/settings/settings.html'),
223 defaults=errors.value,
223 defaults=errors.value,
224 errors=errors.error_dict or {},
224 errors=errors.error_dict or {},
225 prefix_error=False,
225 prefix_error=False,
226 encoding="UTF-8"
226 encoding="UTF-8"
227 )
227 )
228
228
229 try:
229 try:
230 # fix namespaces for hooks and extensions
230 # fix namespaces for hooks and extensions
231 _f = lambda s: s.replace('.', '_')
231 _f = lambda s: s.replace('.', '_')
232
232
233 sett = RhodeCodeUi.get_by_key('push_ssl')
233 sett = RhodeCodeUi.get_by_key('push_ssl')
234 sett.ui_value = form_result['web_push_ssl']
234 sett.ui_value = form_result['web_push_ssl']
235 Session().add(sett)
235 Session().add(sett)
236
236
237 sett = RhodeCodeUi.get_by_key('/')
237 sett = RhodeCodeUi.get_by_key('/')
238 sett.ui_value = form_result['paths_root_path']
238 sett.ui_value = form_result['paths_root_path']
239 Session().add(sett)
239 Session().add(sett)
240
240
241 #HOOKS
241 #HOOKS
242 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_UPDATE)
242 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_UPDATE)
243 sett.ui_active = form_result[_f('hooks_%s' %
243 sett.ui_active = form_result[_f('hooks_%s' %
244 RhodeCodeUi.HOOK_UPDATE)]
244 RhodeCodeUi.HOOK_UPDATE)]
245 Session().add(sett)
245 Session().add(sett)
246
246
247 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_REPO_SIZE)
247 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_REPO_SIZE)
248 sett.ui_active = form_result[_f('hooks_%s' %
248 sett.ui_active = form_result[_f('hooks_%s' %
249 RhodeCodeUi.HOOK_REPO_SIZE)]
249 RhodeCodeUi.HOOK_REPO_SIZE)]
250 Session().add(sett)
250 Session().add(sett)
251
251
252 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_PUSH)
252 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_PUSH)
253 sett.ui_active = form_result[_f('hooks_%s' %
253 sett.ui_active = form_result[_f('hooks_%s' %
254 RhodeCodeUi.HOOK_PUSH)]
254 RhodeCodeUi.HOOK_PUSH)]
255 Session().add(sett)
255 Session().add(sett)
256
256
257 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_PULL)
257 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_PULL)
258 sett.ui_active = form_result[_f('hooks_%s' %
258 sett.ui_active = form_result[_f('hooks_%s' %
259 RhodeCodeUi.HOOK_PULL)]
259 RhodeCodeUi.HOOK_PULL)]
260
260
261 Session().add(sett)
261 Session().add(sett)
262
262
263 ## EXTENSIONS
263 ## EXTENSIONS
264 sett = RhodeCodeUi.get_by_key('largefiles')
264 sett = RhodeCodeUi.get_by_key('largefiles')
265 if not sett:
265 if not sett:
266 #make one if it's not there !
266 #make one if it's not there !
267 sett = RhodeCodeUi()
267 sett = RhodeCodeUi()
268 sett.ui_key = 'largefiles'
268 sett.ui_key = 'largefiles'
269 sett.ui_section = 'extensions'
269 sett.ui_section = 'extensions'
270 sett.ui_active = form_result[_f('extensions_largefiles')]
270 sett.ui_active = form_result[_f('extensions_largefiles')]
271 Session().add(sett)
271 Session().add(sett)
272
272
273 sett = RhodeCodeUi.get_by_key('hgsubversion')
273 sett = RhodeCodeUi.get_by_key('hgsubversion')
274 if not sett:
274 if not sett:
275 #make one if it's not there !
275 #make one if it's not there !
276 sett = RhodeCodeUi()
276 sett = RhodeCodeUi()
277 sett.ui_key = 'hgsubversion'
277 sett.ui_key = 'hgsubversion'
278 sett.ui_section = 'extensions'
278 sett.ui_section = 'extensions'
279
279
280 sett.ui_active = form_result[_f('extensions_hgsubversion')]
280 sett.ui_active = form_result[_f('extensions_hgsubversion')]
281 Session().add(sett)
281 Session().add(sett)
282
282
283 # sett = RhodeCodeUi.get_by_key('hggit')
283 # sett = RhodeCodeUi.get_by_key('hggit')
284 # if not sett:
284 # if not sett:
285 # #make one if it's not there !
285 # #make one if it's not there !
286 # sett = RhodeCodeUi()
286 # sett = RhodeCodeUi()
287 # sett.ui_key = 'hggit'
287 # sett.ui_key = 'hggit'
288 # sett.ui_section = 'extensions'
288 # sett.ui_section = 'extensions'
289 #
289 #
290 # sett.ui_active = form_result[_f('extensions_hggit')]
290 # sett.ui_active = form_result[_f('extensions_hggit')]
291 # Session().add(sett)
291 # Session().add(sett)
292
292
293 Session().commit()
293 Session().commit()
294
294
295 h.flash(_('Updated VCS settings'), category='success')
295 h.flash(_('Updated VCS settings'), category='success')
296
296
297 except Exception:
297 except Exception:
298 log.error(traceback.format_exc())
298 log.error(traceback.format_exc())
299 h.flash(_('error occurred during updating '
299 h.flash(_('error occurred during updating '
300 'application settings'), category='error')
300 'application settings'), category='error')
301
301
302 if setting_id == 'hooks':
302 if setting_id == 'hooks':
303 ui_key = request.POST.get('new_hook_ui_key')
303 ui_key = request.POST.get('new_hook_ui_key')
304 ui_value = request.POST.get('new_hook_ui_value')
304 ui_value = request.POST.get('new_hook_ui_value')
305 try:
305 try:
306
306
307 if ui_value and ui_key:
307 if ui_value and ui_key:
308 RhodeCodeUi.create_or_update_hook(ui_key, ui_value)
308 RhodeCodeUi.create_or_update_hook(ui_key, ui_value)
309 h.flash(_('Added new hook'),
309 h.flash(_('Added new hook'),
310 category='success')
310 category='success')
311
311
312 # check for edits
312 # check for edits
313 update = False
313 update = False
314 _d = request.POST.dict_of_lists()
314 _d = request.POST.dict_of_lists()
315 for k, v in zip(_d.get('hook_ui_key', []),
315 for k, v in zip(_d.get('hook_ui_key', []),
316 _d.get('hook_ui_value_new', [])):
316 _d.get('hook_ui_value_new', [])):
317 RhodeCodeUi.create_or_update_hook(k, v)
317 RhodeCodeUi.create_or_update_hook(k, v)
318 update = True
318 update = True
319
319
320 if update:
320 if update:
321 h.flash(_('Updated hooks'), category='success')
321 h.flash(_('Updated hooks'), category='success')
322 Session().commit()
322 Session().commit()
323 except Exception:
323 except Exception:
324 log.error(traceback.format_exc())
324 log.error(traceback.format_exc())
325 h.flash(_('error occurred during hook creation'),
325 h.flash(_('error occurred during hook creation'),
326 category='error')
326 category='error')
327
327
328 return redirect(url('admin_edit_setting', setting_id='hooks'))
328 return redirect(url('admin_edit_setting', setting_id='hooks'))
329
329
330 if setting_id == 'email':
330 if setting_id == 'email':
331 test_email = request.POST.get('test_email')
331 test_email = request.POST.get('test_email')
332 test_email_subj = 'RhodeCode TestEmail'
332 test_email_subj = 'RhodeCode TestEmail'
333 test_email_body = 'RhodeCode Email test'
333 test_email_body = 'RhodeCode Email test'
334
334
335 test_email_html_body = EmailNotificationModel()\
335 test_email_html_body = EmailNotificationModel()\
336 .get_email_tmpl(EmailNotificationModel.TYPE_DEFAULT,
336 .get_email_tmpl(EmailNotificationModel.TYPE_DEFAULT,
337 body=test_email_body)
337 body=test_email_body)
338
338
339 recipients = [test_email] if [test_email] else None
339 recipients = [test_email] if [test_email] else None
340
340
341 run_task(tasks.send_email, recipients, test_email_subj,
341 run_task(tasks.send_email, recipients, test_email_subj,
342 test_email_body, test_email_html_body)
342 test_email_body, test_email_html_body)
343
343
344 h.flash(_('Email task created'), category='success')
344 h.flash(_('Email task created'), category='success')
345 return redirect(url('admin_settings'))
345 return redirect(url('admin_settings'))
346
346
347 @HasPermissionAllDecorator('hg.admin')
347 @HasPermissionAllDecorator('hg.admin')
348 def delete(self, setting_id):
348 def delete(self, setting_id):
349 """DELETE /admin/settings/setting_id: Delete an existing item"""
349 """DELETE /admin/settings/setting_id: Delete an existing item"""
350 # Forms posted to this method should contain a hidden field:
350 # Forms posted to this method should contain a hidden field:
351 # <input type="hidden" name="_method" value="DELETE" />
351 # <input type="hidden" name="_method" value="DELETE" />
352 # Or using helpers:
352 # Or using helpers:
353 # h.form(url('admin_setting', setting_id=ID),
353 # h.form(url('admin_setting', setting_id=ID),
354 # method='delete')
354 # method='delete')
355 # url('admin_setting', setting_id=ID)
355 # url('admin_setting', setting_id=ID)
356 if setting_id == 'hooks':
356 if setting_id == 'hooks':
357 hook_id = request.POST.get('hook_id')
357 hook_id = request.POST.get('hook_id')
358 RhodeCodeUi.delete(hook_id)
358 RhodeCodeUi.delete(hook_id)
359 Session().commit()
359 Session().commit()
360
360
361 @HasPermissionAllDecorator('hg.admin')
361 @HasPermissionAllDecorator('hg.admin')
362 def show(self, setting_id, format='html'):
362 def show(self, setting_id, format='html'):
363 """
363 """
364 GET /admin/settings/setting_id: Show a specific item"""
364 GET /admin/settings/setting_id: Show a specific item"""
365 # url('admin_setting', setting_id=ID)
365 # url('admin_setting', setting_id=ID)
366
366
367 @HasPermissionAllDecorator('hg.admin')
367 @HasPermissionAllDecorator('hg.admin')
368 def edit(self, setting_id, format='html'):
368 def edit(self, setting_id, format='html'):
369 """
369 """
370 GET /admin/settings/setting_id/edit: Form to
370 GET /admin/settings/setting_id/edit: Form to
371 edit an existing item"""
371 edit an existing item"""
372 # url('admin_edit_setting', setting_id=ID)
372 # url('admin_edit_setting', setting_id=ID)
373
373
374 c.hooks = RhodeCodeUi.get_builtin_hooks()
374 c.hooks = RhodeCodeUi.get_builtin_hooks()
375 c.custom_hooks = RhodeCodeUi.get_custom_hooks()
375 c.custom_hooks = RhodeCodeUi.get_custom_hooks()
376
376
377 return htmlfill.render(
377 return htmlfill.render(
378 render('admin/settings/hooks.html'),
378 render('admin/settings/hooks.html'),
379 defaults={},
379 defaults={},
380 encoding="UTF-8",
380 encoding="UTF-8",
381 force_defaults=False
381 force_defaults=False
382 )
382 )
383
383
384 @NotAnonymous()
384 @NotAnonymous()
385 def my_account(self):
385 def my_account(self):
386 """
386 """
387 GET /_admin/my_account Displays info about my account
387 GET /_admin/my_account Displays info about my account
388 """
388 """
389 # url('admin_settings_my_account')
389 # url('admin_settings_my_account')
390
390
391 c.user = User.get(self.rhodecode_user.user_id)
391 c.user = User.get(self.rhodecode_user.user_id)
392 all_repos = Session().query(Repository)\
392 all_repos = Session().query(Repository)\
393 .filter(Repository.user_id == c.user.user_id)\
393 .filter(Repository.user_id == c.user.user_id)\
394 .order_by(func.lower(Repository.repo_name)).all()
394 .order_by(func.lower(Repository.repo_name)).all()
395
395
396 c.user_repos = ScmModel().get_repos(all_repos)
396 c.user_repos = ScmModel().get_repos(all_repos)
397
397
398 if c.user.username == 'default':
398 if c.user.username == 'default':
399 h.flash(_("You can't edit this user since it's"
399 h.flash(_("You can't edit this user since it's"
400 " crucial for entire application"), category='warning')
400 " crucial for entire application"), category='warning')
401 return redirect(url('users'))
401 return redirect(url('users'))
402
402
403 defaults = c.user.get_dict()
403 defaults = c.user.get_dict()
404
404
405 c.form = htmlfill.render(
405 c.form = htmlfill.render(
406 render('admin/users/user_edit_my_account_form.html'),
406 render('admin/users/user_edit_my_account_form.html'),
407 defaults=defaults,
407 defaults=defaults,
408 encoding="UTF-8",
408 encoding="UTF-8",
409 force_defaults=False
409 force_defaults=False
410 )
410 )
411 return render('admin/users/user_edit_my_account.html')
411 return render('admin/users/user_edit_my_account.html')
412
412
413 @NotAnonymous()
413 @NotAnonymous()
414 def my_account_update(self):
414 def my_account_update(self):
415 """PUT /_admin/my_account_update: Update an existing item"""
415 """PUT /_admin/my_account_update: Update an existing item"""
416 # Forms posted to this method should contain a hidden field:
416 # Forms posted to this method should contain a hidden field:
417 # <input type="hidden" name="_method" value="PUT" />
417 # <input type="hidden" name="_method" value="PUT" />
418 # Or using helpers:
418 # Or using helpers:
419 # h.form(url('admin_settings_my_account_update'),
419 # h.form(url('admin_settings_my_account_update'),
420 # method='put')
420 # method='put')
421 # url('admin_settings_my_account_update', id=ID)
421 # url('admin_settings_my_account_update', id=ID)
422 uid = self.rhodecode_user.user_id
422 uid = self.rhodecode_user.user_id
423 email = self.rhodecode_user.email
423 email = self.rhodecode_user.email
424 _form = UserForm(edit=True,
424 _form = UserForm(edit=True,
425 old_data={'user_id': uid, 'email': email})()
425 old_data={'user_id': uid, 'email': email})()
426 form_result = {}
426 form_result = {}
427 try:
427 try:
428 form_result = _form.to_python(dict(request.POST))
428 form_result = _form.to_python(dict(request.POST))
429 UserModel().update_my_account(uid, form_result)
429 UserModel().update_my_account(uid, form_result)
430 h.flash(_('Your account was updated successfully'),
430 h.flash(_('Your account was updated successfully'),
431 category='success')
431 category='success')
432 Session().commit()
432 Session().commit()
433 except formencode.Invalid, errors:
433 except formencode.Invalid, errors:
434 c.user = User.get(self.rhodecode_user.user_id)
434 c.user = User.get(self.rhodecode_user.user_id)
435
435
436 c.form = htmlfill.render(
436 c.form = htmlfill.render(
437 render('admin/users/user_edit_my_account_form.html'),
437 render('admin/users/user_edit_my_account_form.html'),
438 defaults=errors.value,
438 defaults=errors.value,
439 errors=errors.error_dict or {},
439 errors=errors.error_dict or {},
440 prefix_error=False,
440 prefix_error=False,
441 encoding="UTF-8")
441 encoding="UTF-8")
442 return render('admin/users/user_edit_my_account.html')
442 return render('admin/users/user_edit_my_account.html')
443 except Exception:
443 except Exception:
444 log.error(traceback.format_exc())
444 log.error(traceback.format_exc())
445 h.flash(_('error occurred during update of user %s') \
445 h.flash(_('error occurred during update of user %s') \
446 % form_result.get('username'), category='error')
446 % form_result.get('username'), category='error')
447
447
448 return redirect(url('my_account'))
448 return redirect(url('my_account'))
449
449
450 @NotAnonymous()
450 @NotAnonymous()
451 def my_account_my_repos(self):
451 def my_account_my_repos(self):
452 all_repos = Session().query(Repository)\
452 all_repos = Session().query(Repository)\
453 .filter(Repository.user_id == self.rhodecode_user.user_id)\
453 .filter(Repository.user_id == self.rhodecode_user.user_id)\
454 .order_by(func.lower(Repository.repo_name))\
454 .order_by(func.lower(Repository.repo_name))\
455 .all()
455 .all()
456 c.user_repos = ScmModel().get_repos(all_repos)
456 c.user_repos = ScmModel().get_repos(all_repos)
457 return render('admin/users/user_edit_my_account_repos.html')
457 return render('admin/users/user_edit_my_account_repos.html')
458
458
459 @NotAnonymous()
459 @NotAnonymous()
460 def my_account_my_pullrequests(self):
460 def my_account_my_pullrequests(self):
461 c.my_pull_requests = PullRequest.query()\
461 c.my_pull_requests = PullRequest.query()\
462 .filter(PullRequest.user_id==
462 .filter(PullRequest.user_id==
463 self.rhodecode_user.user_id)\
463 self.rhodecode_user.user_id)\
464 .all()
464 .all()
465 c.participate_in_pull_requests = \
465 c.participate_in_pull_requests = \
466 [x.pull_request for x in PullRequestReviewers.query()\
466 [x.pull_request for x in PullRequestReviewers.query()\
467 .filter(PullRequestReviewers.user_id==
467 .filter(PullRequestReviewers.user_id==
468 self.rhodecode_user.user_id)\
468 self.rhodecode_user.user_id)\
469 .all()]
469 .all()]
470 return render('admin/users/user_edit_my_account_pullrequests.html')
470 return render('admin/users/user_edit_my_account_pullrequests.html')
471
471
472 @NotAnonymous()
472 @NotAnonymous()
473 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
473 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
474 def create_repository(self):
474 def create_repository(self):
475 """GET /_admin/create_repository: Form to create a new item"""
475 """GET /_admin/create_repository: Form to create a new item"""
476
476
477 c.repo_groups = RepoGroup.groups_choices(check_perms=True)
477 c.repo_groups = RepoGroup.groups_choices(check_perms=True)
478 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
478 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
479 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
479 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
480
480
481 new_repo = request.GET.get('repo', '')
481 new_repo = request.GET.get('repo', '')
482 c.new_repo = repo_name_slug(new_repo)
482 c.new_repo = repo_name_slug(new_repo)
483
483
484 return render('admin/repos/repo_add_create_repository.html')
484 ## apply the defaults from defaults page
485 defaults = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
486 return htmlfill.render(
487 render('admin/repos/repo_add_create_repository.html'),
488 defaults=defaults,
489 errors={},
490 prefix_error=False,
491 encoding="UTF-8"
492 )
485
493
486 def _get_hg_ui_settings(self):
494 def _get_hg_ui_settings(self):
487 ret = RhodeCodeUi.query().all()
495 ret = RhodeCodeUi.query().all()
488
496
489 if not ret:
497 if not ret:
490 raise Exception('Could not get application ui settings !')
498 raise Exception('Could not get application ui settings !')
491 settings = {}
499 settings = {}
492 for each in ret:
500 for each in ret:
493 k = each.ui_key
501 k = each.ui_key
494 v = each.ui_value
502 v = each.ui_value
495 if k == '/':
503 if k == '/':
496 k = 'root_path'
504 k = 'root_path'
497
505
498 if k == 'push_ssl':
506 if k == 'push_ssl':
499 v = str2bool(v)
507 v = str2bool(v)
500
508
501 if k.find('.') != -1:
509 if k.find('.') != -1:
502 k = k.replace('.', '_')
510 k = k.replace('.', '_')
503
511
504 if each.ui_section in ['hooks', 'extensions']:
512 if each.ui_section in ['hooks', 'extensions']:
505 v = each.ui_active
513 v = each.ui_active
506
514
507 settings[each.ui_section + '_' + k] = v
515 settings[each.ui_section + '_' + k] = v
508 return settings
516 return settings
@@ -1,683 +1,701 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.db_manage
3 rhodecode.lib.db_manage
4 ~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Database creation, and setup module for RhodeCode. Used for creation
6 Database creation, and setup module for RhodeCode. Used for creation
7 of database as well as for migration operations
7 of database as well as for migration operations
8
8
9 :created_on: Apr 10, 2010
9 :created_on: Apr 10, 2010
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26
26
27 import os
27 import os
28 import sys
28 import sys
29 import uuid
29 import uuid
30 import logging
30 import logging
31 from os.path import dirname as dn, join as jn
31 from os.path import dirname as dn, join as jn
32
32
33 from rhodecode import __dbversion__, __py_version__
33 from rhodecode import __dbversion__, __py_version__
34
34
35 from rhodecode.model.user import UserModel
35 from rhodecode.model.user import UserModel
36 from rhodecode.lib.utils import ask_ok
36 from rhodecode.lib.utils import ask_ok
37 from rhodecode.model import init_model
37 from rhodecode.model import init_model
38 from rhodecode.model.db import User, Permission, RhodeCodeUi, \
38 from rhodecode.model.db import User, Permission, RhodeCodeUi, \
39 RhodeCodeSetting, UserToPerm, DbMigrateVersion, RepoGroup, \
39 RhodeCodeSetting, UserToPerm, DbMigrateVersion, RepoGroup, \
40 UserRepoGroupToPerm
40 UserRepoGroupToPerm
41
41
42 from sqlalchemy.engine import create_engine
42 from sqlalchemy.engine import create_engine
43 from rhodecode.model.repos_group import ReposGroupModel
43 from rhodecode.model.repos_group import ReposGroupModel
44 #from rhodecode.model import meta
44 #from rhodecode.model import meta
45 from rhodecode.model.meta import Session, Base
45 from rhodecode.model.meta import Session, Base
46
46
47
47
48 log = logging.getLogger(__name__)
48 log = logging.getLogger(__name__)
49
49
50
50
51 def notify(msg):
51 def notify(msg):
52 """
52 """
53 Notification for migrations messages
53 Notification for migrations messages
54 """
54 """
55 ml = len(msg) + (4 * 2)
55 ml = len(msg) + (4 * 2)
56 print >> sys.stdout, ('*** %s ***\n%s' % (msg, '*' * ml)).upper()
56 print >> sys.stdout, ('*** %s ***\n%s' % (msg, '*' * ml)).upper()
57
57
58
58
59 class DbManage(object):
59 class DbManage(object):
60 def __init__(self, log_sql, dbconf, root, tests=False, cli_args={}):
60 def __init__(self, log_sql, dbconf, root, tests=False, cli_args={}):
61 self.dbname = dbconf.split('/')[-1]
61 self.dbname = dbconf.split('/')[-1]
62 self.tests = tests
62 self.tests = tests
63 self.root = root
63 self.root = root
64 self.dburi = dbconf
64 self.dburi = dbconf
65 self.log_sql = log_sql
65 self.log_sql = log_sql
66 self.db_exists = False
66 self.db_exists = False
67 self.cli_args = cli_args
67 self.cli_args = cli_args
68 self.init_db()
68 self.init_db()
69 global ask_ok
69 global ask_ok
70
70
71 if self.cli_args.get('force_ask') is True:
71 if self.cli_args.get('force_ask') is True:
72 ask_ok = lambda *args, **kwargs: True
72 ask_ok = lambda *args, **kwargs: True
73 elif self.cli_args.get('force_ask') is False:
73 elif self.cli_args.get('force_ask') is False:
74 ask_ok = lambda *args, **kwargs: False
74 ask_ok = lambda *args, **kwargs: False
75
75
76 def init_db(self):
76 def init_db(self):
77 engine = create_engine(self.dburi, echo=self.log_sql)
77 engine = create_engine(self.dburi, echo=self.log_sql)
78 init_model(engine)
78 init_model(engine)
79 self.sa = Session()
79 self.sa = Session()
80
80
81 def create_tables(self, override=False):
81 def create_tables(self, override=False):
82 """
82 """
83 Create a auth database
83 Create a auth database
84 """
84 """
85
85
86 log.info("Any existing database is going to be destroyed")
86 log.info("Any existing database is going to be destroyed")
87 if self.tests:
87 if self.tests:
88 destroy = True
88 destroy = True
89 else:
89 else:
90 destroy = ask_ok('Are you sure to destroy old database ? [y/n]')
90 destroy = ask_ok('Are you sure to destroy old database ? [y/n]')
91 if not destroy:
91 if not destroy:
92 sys.exit('Nothing done')
92 sys.exit('Nothing done')
93 if destroy:
93 if destroy:
94 Base.metadata.drop_all()
94 Base.metadata.drop_all()
95
95
96 checkfirst = not override
96 checkfirst = not override
97 Base.metadata.create_all(checkfirst=checkfirst)
97 Base.metadata.create_all(checkfirst=checkfirst)
98 log.info('Created tables for %s' % self.dbname)
98 log.info('Created tables for %s' % self.dbname)
99
99
100 def set_db_version(self):
100 def set_db_version(self):
101 ver = DbMigrateVersion()
101 ver = DbMigrateVersion()
102 ver.version = __dbversion__
102 ver.version = __dbversion__
103 ver.repository_id = 'rhodecode_db_migrations'
103 ver.repository_id = 'rhodecode_db_migrations'
104 ver.repository_path = 'versions'
104 ver.repository_path = 'versions'
105 self.sa.add(ver)
105 self.sa.add(ver)
106 log.info('db version set to: %s' % __dbversion__)
106 log.info('db version set to: %s' % __dbversion__)
107
107
108 def upgrade(self):
108 def upgrade(self):
109 """
109 """
110 Upgrades given database schema to given revision following
110 Upgrades given database schema to given revision following
111 all needed steps, to perform the upgrade
111 all needed steps, to perform the upgrade
112
112
113 """
113 """
114
114
115 from rhodecode.lib.dbmigrate.migrate.versioning import api
115 from rhodecode.lib.dbmigrate.migrate.versioning import api
116 from rhodecode.lib.dbmigrate.migrate.exceptions import \
116 from rhodecode.lib.dbmigrate.migrate.exceptions import \
117 DatabaseNotControlledError
117 DatabaseNotControlledError
118
118
119 if 'sqlite' in self.dburi:
119 if 'sqlite' in self.dburi:
120 print (
120 print (
121 '********************** WARNING **********************\n'
121 '********************** WARNING **********************\n'
122 'Make sure your version of sqlite is at least 3.7.X. \n'
122 'Make sure your version of sqlite is at least 3.7.X. \n'
123 'Earlier versions are known to fail on some migrations\n'
123 'Earlier versions are known to fail on some migrations\n'
124 '*****************************************************\n'
124 '*****************************************************\n'
125 )
125 )
126 upgrade = ask_ok('You are about to perform database upgrade, make '
126 upgrade = ask_ok('You are about to perform database upgrade, make '
127 'sure You backed up your database before. '
127 'sure You backed up your database before. '
128 'Continue ? [y/n]')
128 'Continue ? [y/n]')
129 if not upgrade:
129 if not upgrade:
130 sys.exit('Nothing done')
130 sys.exit('Nothing done')
131
131
132 repository_path = jn(dn(dn(dn(os.path.realpath(__file__)))),
132 repository_path = jn(dn(dn(dn(os.path.realpath(__file__)))),
133 'rhodecode/lib/dbmigrate')
133 'rhodecode/lib/dbmigrate')
134 db_uri = self.dburi
134 db_uri = self.dburi
135
135
136 try:
136 try:
137 curr_version = api.db_version(db_uri, repository_path)
137 curr_version = api.db_version(db_uri, repository_path)
138 msg = ('Found current database under version'
138 msg = ('Found current database under version'
139 ' control with version %s' % curr_version)
139 ' control with version %s' % curr_version)
140
140
141 except (RuntimeError, DatabaseNotControlledError):
141 except (RuntimeError, DatabaseNotControlledError):
142 curr_version = 1
142 curr_version = 1
143 msg = ('Current database is not under version control. Setting'
143 msg = ('Current database is not under version control. Setting'
144 ' as version %s' % curr_version)
144 ' as version %s' % curr_version)
145 api.version_control(db_uri, repository_path, curr_version)
145 api.version_control(db_uri, repository_path, curr_version)
146
146
147 notify(msg)
147 notify(msg)
148
148
149 if curr_version == __dbversion__:
149 if curr_version == __dbversion__:
150 sys.exit('This database is already at the newest version')
150 sys.exit('This database is already at the newest version')
151
151
152 #======================================================================
152 #======================================================================
153 # UPGRADE STEPS
153 # UPGRADE STEPS
154 #======================================================================
154 #======================================================================
155
155
156 class UpgradeSteps(object):
156 class UpgradeSteps(object):
157 """
157 """
158 Those steps follow schema versions so for example schema
158 Those steps follow schema versions so for example schema
159 for example schema with seq 002 == step_2 and so on.
159 for example schema with seq 002 == step_2 and so on.
160 """
160 """
161
161
162 def __init__(self, klass):
162 def __init__(self, klass):
163 self.klass = klass
163 self.klass = klass
164
164
165 def step_0(self):
165 def step_0(self):
166 # step 0 is the schema upgrade, and than follow proper upgrades
166 # step 0 is the schema upgrade, and than follow proper upgrades
167 notify('attempting to do database upgrade to version %s' \
167 notify('attempting to do database upgrade to version %s' \
168 % __dbversion__)
168 % __dbversion__)
169 api.upgrade(db_uri, repository_path, __dbversion__)
169 api.upgrade(db_uri, repository_path, __dbversion__)
170 notify('Schema upgrade completed')
170 notify('Schema upgrade completed')
171
171
172 def step_1(self):
172 def step_1(self):
173 pass
173 pass
174
174
175 def step_2(self):
175 def step_2(self):
176 notify('Patching repo paths for newer version of RhodeCode')
176 notify('Patching repo paths for newer version of RhodeCode')
177 self.klass.fix_repo_paths()
177 self.klass.fix_repo_paths()
178
178
179 notify('Patching default user of RhodeCode')
179 notify('Patching default user of RhodeCode')
180 self.klass.fix_default_user()
180 self.klass.fix_default_user()
181
181
182 log.info('Changing ui settings')
182 log.info('Changing ui settings')
183 self.klass.create_ui_settings()
183 self.klass.create_ui_settings()
184
184
185 def step_3(self):
185 def step_3(self):
186 notify('Adding additional settings into RhodeCode db')
186 notify('Adding additional settings into RhodeCode db')
187 self.klass.fix_settings()
187 self.klass.fix_settings()
188 notify('Adding ldap defaults')
188 notify('Adding ldap defaults')
189 self.klass.create_ldap_options(skip_existing=True)
189 self.klass.create_ldap_options(skip_existing=True)
190
190
191 def step_4(self):
191 def step_4(self):
192 notify('create permissions and fix groups')
192 notify('create permissions and fix groups')
193 self.klass.create_permissions()
193 self.klass.create_permissions()
194 self.klass.fixup_groups()
194 self.klass.fixup_groups()
195
195
196 def step_5(self):
196 def step_5(self):
197 pass
197 pass
198
198
199 def step_6(self):
199 def step_6(self):
200
200
201 notify('re-checking permissions')
201 notify('re-checking permissions')
202 self.klass.create_permissions()
202 self.klass.create_permissions()
203
203
204 notify('installing new UI options')
204 notify('installing new UI options')
205 sett4 = RhodeCodeSetting('show_public_icon', True)
205 sett4 = RhodeCodeSetting('show_public_icon', True)
206 Session().add(sett4)
206 Session().add(sett4)
207 sett5 = RhodeCodeSetting('show_private_icon', True)
207 sett5 = RhodeCodeSetting('show_private_icon', True)
208 Session().add(sett5)
208 Session().add(sett5)
209 sett6 = RhodeCodeSetting('stylify_metatags', False)
209 sett6 = RhodeCodeSetting('stylify_metatags', False)
210 Session().add(sett6)
210 Session().add(sett6)
211
211
212 notify('fixing old PULL hook')
212 notify('fixing old PULL hook')
213 _pull = RhodeCodeUi.get_by_key('preoutgoing.pull_logger')
213 _pull = RhodeCodeUi.get_by_key('preoutgoing.pull_logger')
214 if _pull:
214 if _pull:
215 _pull.ui_key = RhodeCodeUi.HOOK_PULL
215 _pull.ui_key = RhodeCodeUi.HOOK_PULL
216 Session().add(_pull)
216 Session().add(_pull)
217
217
218 notify('fixing old PUSH hook')
218 notify('fixing old PUSH hook')
219 _push = RhodeCodeUi.get_by_key('pretxnchangegroup.push_logger')
219 _push = RhodeCodeUi.get_by_key('pretxnchangegroup.push_logger')
220 if _push:
220 if _push:
221 _push.ui_key = RhodeCodeUi.HOOK_PUSH
221 _push.ui_key = RhodeCodeUi.HOOK_PUSH
222 Session().add(_push)
222 Session().add(_push)
223
223
224 notify('installing new pre-push hook')
224 notify('installing new pre-push hook')
225 hooks4 = RhodeCodeUi()
225 hooks4 = RhodeCodeUi()
226 hooks4.ui_section = 'hooks'
226 hooks4.ui_section = 'hooks'
227 hooks4.ui_key = RhodeCodeUi.HOOK_PRE_PUSH
227 hooks4.ui_key = RhodeCodeUi.HOOK_PRE_PUSH
228 hooks4.ui_value = 'python:rhodecode.lib.hooks.pre_push'
228 hooks4.ui_value = 'python:rhodecode.lib.hooks.pre_push'
229 Session().add(hooks4)
229 Session().add(hooks4)
230
230
231 notify('installing new pre-pull hook')
231 notify('installing new pre-pull hook')
232 hooks6 = RhodeCodeUi()
232 hooks6 = RhodeCodeUi()
233 hooks6.ui_section = 'hooks'
233 hooks6.ui_section = 'hooks'
234 hooks6.ui_key = RhodeCodeUi.HOOK_PRE_PULL
234 hooks6.ui_key = RhodeCodeUi.HOOK_PRE_PULL
235 hooks6.ui_value = 'python:rhodecode.lib.hooks.pre_pull'
235 hooks6.ui_value = 'python:rhodecode.lib.hooks.pre_pull'
236 Session().add(hooks6)
236 Session().add(hooks6)
237
237
238 notify('installing hgsubversion option')
238 notify('installing hgsubversion option')
239 # enable hgsubversion disabled by default
239 # enable hgsubversion disabled by default
240 hgsubversion = RhodeCodeUi()
240 hgsubversion = RhodeCodeUi()
241 hgsubversion.ui_section = 'extensions'
241 hgsubversion.ui_section = 'extensions'
242 hgsubversion.ui_key = 'hgsubversion'
242 hgsubversion.ui_key = 'hgsubversion'
243 hgsubversion.ui_value = ''
243 hgsubversion.ui_value = ''
244 hgsubversion.ui_active = False
244 hgsubversion.ui_active = False
245 Session().add(hgsubversion)
245 Session().add(hgsubversion)
246
246
247 notify('installing hg git option')
247 notify('installing hg git option')
248 # enable hggit disabled by default
248 # enable hggit disabled by default
249 hggit = RhodeCodeUi()
249 hggit = RhodeCodeUi()
250 hggit.ui_section = 'extensions'
250 hggit.ui_section = 'extensions'
251 hggit.ui_key = 'hggit'
251 hggit.ui_key = 'hggit'
252 hggit.ui_value = ''
252 hggit.ui_value = ''
253 hggit.ui_active = False
253 hggit.ui_active = False
254 Session().add(hggit)
254 Session().add(hggit)
255
255
256 notify('re-check default permissions')
256 notify('re-check default permissions')
257 default_user = User.get_by_username(User.DEFAULT_USER)
257 default_user = User.get_by_username(User.DEFAULT_USER)
258 perm = Permission.get_by_key('hg.fork.repository')
258 perm = Permission.get_by_key('hg.fork.repository')
259 reg_perm = UserToPerm()
259 reg_perm = UserToPerm()
260 reg_perm.user = default_user
260 reg_perm.user = default_user
261 reg_perm.permission = perm
261 reg_perm.permission = perm
262 Session().add(reg_perm)
262 Session().add(reg_perm)
263
263
264 def step_7(self):
264 def step_7(self):
265 perm_fixes = self.klass.reset_permissions(User.DEFAULT_USER)
265 perm_fixes = self.klass.reset_permissions(User.DEFAULT_USER)
266 Session().commit()
266 Session().commit()
267 if perm_fixes:
267 if perm_fixes:
268 notify('There was an inconsistent state of permissions '
268 notify('There was an inconsistent state of permissions '
269 'detected for default user. Permissions are now '
269 'detected for default user. Permissions are now '
270 'reset to the default value for default user. '
270 'reset to the default value for default user. '
271 'Please validate and check default permissions '
271 'Please validate and check default permissions '
272 'in admin panel')
272 'in admin panel')
273
273
274 def step_8(self):
274 def step_8(self):
275 self.klass.populate_default_permissions()
275 self.klass.populate_default_permissions()
276 self.klass.create_default_options(skip_existing=True)
276 Session().commit()
277 Session().commit()
277
278
278 upgrade_steps = [0] + range(curr_version + 1, __dbversion__ + 1)
279 upgrade_steps = [0] + range(curr_version + 1, __dbversion__ + 1)
279
280
280 # CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
281 # CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
281 _step = None
282 _step = None
282 for step in upgrade_steps:
283 for step in upgrade_steps:
283 notify('performing upgrade step %s' % step)
284 notify('performing upgrade step %s' % step)
284 getattr(UpgradeSteps(self), 'step_%s' % step)()
285 getattr(UpgradeSteps(self), 'step_%s' % step)()
285 self.sa.commit()
286 self.sa.commit()
286 _step = step
287 _step = step
287
288
288 notify('upgrade to version %s successful' % _step)
289 notify('upgrade to version %s successful' % _step)
289
290
290 def fix_repo_paths(self):
291 def fix_repo_paths(self):
291 """
292 """
292 Fixes a old rhodecode version path into new one without a '*'
293 Fixes a old rhodecode version path into new one without a '*'
293 """
294 """
294
295
295 paths = self.sa.query(RhodeCodeUi)\
296 paths = self.sa.query(RhodeCodeUi)\
296 .filter(RhodeCodeUi.ui_key == '/')\
297 .filter(RhodeCodeUi.ui_key == '/')\
297 .scalar()
298 .scalar()
298
299
299 paths.ui_value = paths.ui_value.replace('*', '')
300 paths.ui_value = paths.ui_value.replace('*', '')
300
301
301 try:
302 try:
302 self.sa.add(paths)
303 self.sa.add(paths)
303 self.sa.commit()
304 self.sa.commit()
304 except:
305 except:
305 self.sa.rollback()
306 self.sa.rollback()
306 raise
307 raise
307
308
308 def fix_default_user(self):
309 def fix_default_user(self):
309 """
310 """
310 Fixes a old default user with some 'nicer' default values,
311 Fixes a old default user with some 'nicer' default values,
311 used mostly for anonymous access
312 used mostly for anonymous access
312 """
313 """
313 def_user = self.sa.query(User)\
314 def_user = self.sa.query(User)\
314 .filter(User.username == 'default')\
315 .filter(User.username == 'default')\
315 .one()
316 .one()
316
317
317 def_user.name = 'Anonymous'
318 def_user.name = 'Anonymous'
318 def_user.lastname = 'User'
319 def_user.lastname = 'User'
319 def_user.email = 'anonymous@rhodecode.org'
320 def_user.email = 'anonymous@rhodecode.org'
320
321
321 try:
322 try:
322 self.sa.add(def_user)
323 self.sa.add(def_user)
323 self.sa.commit()
324 self.sa.commit()
324 except:
325 except:
325 self.sa.rollback()
326 self.sa.rollback()
326 raise
327 raise
327
328
328 def fix_settings(self):
329 def fix_settings(self):
329 """
330 """
330 Fixes rhodecode settings adds ga_code key for google analytics
331 Fixes rhodecode settings adds ga_code key for google analytics
331 """
332 """
332
333
333 hgsettings3 = RhodeCodeSetting('ga_code', '')
334 hgsettings3 = RhodeCodeSetting('ga_code', '')
334
335
335 try:
336 try:
336 self.sa.add(hgsettings3)
337 self.sa.add(hgsettings3)
337 self.sa.commit()
338 self.sa.commit()
338 except:
339 except:
339 self.sa.rollback()
340 self.sa.rollback()
340 raise
341 raise
341
342
342 def admin_prompt(self, second=False):
343 def admin_prompt(self, second=False):
343 if not self.tests:
344 if not self.tests:
344 import getpass
345 import getpass
345
346
346 # defaults
347 # defaults
347 defaults = self.cli_args
348 defaults = self.cli_args
348 username = defaults.get('username')
349 username = defaults.get('username')
349 password = defaults.get('password')
350 password = defaults.get('password')
350 email = defaults.get('email')
351 email = defaults.get('email')
351
352
352 def get_password():
353 def get_password():
353 password = getpass.getpass('Specify admin password '
354 password = getpass.getpass('Specify admin password '
354 '(min 6 chars):')
355 '(min 6 chars):')
355 confirm = getpass.getpass('Confirm password:')
356 confirm = getpass.getpass('Confirm password:')
356
357
357 if password != confirm:
358 if password != confirm:
358 log.error('passwords mismatch')
359 log.error('passwords mismatch')
359 return False
360 return False
360 if len(password) < 6:
361 if len(password) < 6:
361 log.error('password is to short use at least 6 characters')
362 log.error('password is to short use at least 6 characters')
362 return False
363 return False
363
364
364 return password
365 return password
365 if username is None:
366 if username is None:
366 username = raw_input('Specify admin username:')
367 username = raw_input('Specify admin username:')
367 if password is None:
368 if password is None:
368 password = get_password()
369 password = get_password()
369 if not password:
370 if not password:
370 #second try
371 #second try
371 password = get_password()
372 password = get_password()
372 if not password:
373 if not password:
373 sys.exit()
374 sys.exit()
374 if email is None:
375 if email is None:
375 email = raw_input('Specify admin email:')
376 email = raw_input('Specify admin email:')
376 self.create_user(username, password, email, True)
377 self.create_user(username, password, email, True)
377 else:
378 else:
378 log.info('creating admin and regular test users')
379 log.info('creating admin and regular test users')
379 from rhodecode.tests import TEST_USER_ADMIN_LOGIN, \
380 from rhodecode.tests import TEST_USER_ADMIN_LOGIN, \
380 TEST_USER_ADMIN_PASS, TEST_USER_ADMIN_EMAIL, \
381 TEST_USER_ADMIN_PASS, TEST_USER_ADMIN_EMAIL, \
381 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, \
382 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, \
382 TEST_USER_REGULAR_EMAIL, TEST_USER_REGULAR2_LOGIN, \
383 TEST_USER_REGULAR_EMAIL, TEST_USER_REGULAR2_LOGIN, \
383 TEST_USER_REGULAR2_PASS, TEST_USER_REGULAR2_EMAIL
384 TEST_USER_REGULAR2_PASS, TEST_USER_REGULAR2_EMAIL
384
385
385 self.create_user(TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS,
386 self.create_user(TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS,
386 TEST_USER_ADMIN_EMAIL, True)
387 TEST_USER_ADMIN_EMAIL, True)
387
388
388 self.create_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
389 self.create_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
389 TEST_USER_REGULAR_EMAIL, False)
390 TEST_USER_REGULAR_EMAIL, False)
390
391
391 self.create_user(TEST_USER_REGULAR2_LOGIN, TEST_USER_REGULAR2_PASS,
392 self.create_user(TEST_USER_REGULAR2_LOGIN, TEST_USER_REGULAR2_PASS,
392 TEST_USER_REGULAR2_EMAIL, False)
393 TEST_USER_REGULAR2_EMAIL, False)
393
394
394 def create_ui_settings(self):
395 def create_ui_settings(self):
395 """
396 """
396 Creates ui settings, fills out hooks
397 Creates ui settings, fills out hooks
397 and disables dotencode
398 and disables dotencode
398 """
399 """
399
400
400 #HOOKS
401 #HOOKS
401 hooks1_key = RhodeCodeUi.HOOK_UPDATE
402 hooks1_key = RhodeCodeUi.HOOK_UPDATE
402 hooks1_ = self.sa.query(RhodeCodeUi)\
403 hooks1_ = self.sa.query(RhodeCodeUi)\
403 .filter(RhodeCodeUi.ui_key == hooks1_key).scalar()
404 .filter(RhodeCodeUi.ui_key == hooks1_key).scalar()
404
405
405 hooks1 = RhodeCodeUi() if hooks1_ is None else hooks1_
406 hooks1 = RhodeCodeUi() if hooks1_ is None else hooks1_
406 hooks1.ui_section = 'hooks'
407 hooks1.ui_section = 'hooks'
407 hooks1.ui_key = hooks1_key
408 hooks1.ui_key = hooks1_key
408 hooks1.ui_value = 'hg update >&2'
409 hooks1.ui_value = 'hg update >&2'
409 hooks1.ui_active = False
410 hooks1.ui_active = False
410 self.sa.add(hooks1)
411 self.sa.add(hooks1)
411
412
412 hooks2_key = RhodeCodeUi.HOOK_REPO_SIZE
413 hooks2_key = RhodeCodeUi.HOOK_REPO_SIZE
413 hooks2_ = self.sa.query(RhodeCodeUi)\
414 hooks2_ = self.sa.query(RhodeCodeUi)\
414 .filter(RhodeCodeUi.ui_key == hooks2_key).scalar()
415 .filter(RhodeCodeUi.ui_key == hooks2_key).scalar()
415 hooks2 = RhodeCodeUi() if hooks2_ is None else hooks2_
416 hooks2 = RhodeCodeUi() if hooks2_ is None else hooks2_
416 hooks2.ui_section = 'hooks'
417 hooks2.ui_section = 'hooks'
417 hooks2.ui_key = hooks2_key
418 hooks2.ui_key = hooks2_key
418 hooks2.ui_value = 'python:rhodecode.lib.hooks.repo_size'
419 hooks2.ui_value = 'python:rhodecode.lib.hooks.repo_size'
419 self.sa.add(hooks2)
420 self.sa.add(hooks2)
420
421
421 hooks3 = RhodeCodeUi()
422 hooks3 = RhodeCodeUi()
422 hooks3.ui_section = 'hooks'
423 hooks3.ui_section = 'hooks'
423 hooks3.ui_key = RhodeCodeUi.HOOK_PUSH
424 hooks3.ui_key = RhodeCodeUi.HOOK_PUSH
424 hooks3.ui_value = 'python:rhodecode.lib.hooks.log_push_action'
425 hooks3.ui_value = 'python:rhodecode.lib.hooks.log_push_action'
425 self.sa.add(hooks3)
426 self.sa.add(hooks3)
426
427
427 hooks4 = RhodeCodeUi()
428 hooks4 = RhodeCodeUi()
428 hooks4.ui_section = 'hooks'
429 hooks4.ui_section = 'hooks'
429 hooks4.ui_key = RhodeCodeUi.HOOK_PRE_PUSH
430 hooks4.ui_key = RhodeCodeUi.HOOK_PRE_PUSH
430 hooks4.ui_value = 'python:rhodecode.lib.hooks.pre_push'
431 hooks4.ui_value = 'python:rhodecode.lib.hooks.pre_push'
431 self.sa.add(hooks4)
432 self.sa.add(hooks4)
432
433
433 hooks5 = RhodeCodeUi()
434 hooks5 = RhodeCodeUi()
434 hooks5.ui_section = 'hooks'
435 hooks5.ui_section = 'hooks'
435 hooks5.ui_key = RhodeCodeUi.HOOK_PULL
436 hooks5.ui_key = RhodeCodeUi.HOOK_PULL
436 hooks5.ui_value = 'python:rhodecode.lib.hooks.log_pull_action'
437 hooks5.ui_value = 'python:rhodecode.lib.hooks.log_pull_action'
437 self.sa.add(hooks5)
438 self.sa.add(hooks5)
438
439
439 hooks6 = RhodeCodeUi()
440 hooks6 = RhodeCodeUi()
440 hooks6.ui_section = 'hooks'
441 hooks6.ui_section = 'hooks'
441 hooks6.ui_key = RhodeCodeUi.HOOK_PRE_PULL
442 hooks6.ui_key = RhodeCodeUi.HOOK_PRE_PULL
442 hooks6.ui_value = 'python:rhodecode.lib.hooks.pre_pull'
443 hooks6.ui_value = 'python:rhodecode.lib.hooks.pre_pull'
443 self.sa.add(hooks6)
444 self.sa.add(hooks6)
444
445
445 # enable largefiles
446 # enable largefiles
446 largefiles = RhodeCodeUi()
447 largefiles = RhodeCodeUi()
447 largefiles.ui_section = 'extensions'
448 largefiles.ui_section = 'extensions'
448 largefiles.ui_key = 'largefiles'
449 largefiles.ui_key = 'largefiles'
449 largefiles.ui_value = ''
450 largefiles.ui_value = ''
450 self.sa.add(largefiles)
451 self.sa.add(largefiles)
451
452
452 # enable hgsubversion disabled by default
453 # enable hgsubversion disabled by default
453 hgsubversion = RhodeCodeUi()
454 hgsubversion = RhodeCodeUi()
454 hgsubversion.ui_section = 'extensions'
455 hgsubversion.ui_section = 'extensions'
455 hgsubversion.ui_key = 'hgsubversion'
456 hgsubversion.ui_key = 'hgsubversion'
456 hgsubversion.ui_value = ''
457 hgsubversion.ui_value = ''
457 hgsubversion.ui_active = False
458 hgsubversion.ui_active = False
458 self.sa.add(hgsubversion)
459 self.sa.add(hgsubversion)
459
460
460 # enable hggit disabled by default
461 # enable hggit disabled by default
461 hggit = RhodeCodeUi()
462 hggit = RhodeCodeUi()
462 hggit.ui_section = 'extensions'
463 hggit.ui_section = 'extensions'
463 hggit.ui_key = 'hggit'
464 hggit.ui_key = 'hggit'
464 hggit.ui_value = ''
465 hggit.ui_value = ''
465 hggit.ui_active = False
466 hggit.ui_active = False
466 self.sa.add(hggit)
467 self.sa.add(hggit)
467
468
468 def create_ldap_options(self, skip_existing=False):
469 def create_ldap_options(self, skip_existing=False):
469 """Creates ldap settings"""
470 """Creates ldap settings"""
470
471
471 for k, v in [('ldap_active', 'false'), ('ldap_host', ''),
472 for k, v in [('ldap_active', 'false'), ('ldap_host', ''),
472 ('ldap_port', '389'), ('ldap_tls_kind', 'PLAIN'),
473 ('ldap_port', '389'), ('ldap_tls_kind', 'PLAIN'),
473 ('ldap_tls_reqcert', ''), ('ldap_dn_user', ''),
474 ('ldap_tls_reqcert', ''), ('ldap_dn_user', ''),
474 ('ldap_dn_pass', ''), ('ldap_base_dn', ''),
475 ('ldap_dn_pass', ''), ('ldap_base_dn', ''),
475 ('ldap_filter', ''), ('ldap_search_scope', ''),
476 ('ldap_filter', ''), ('ldap_search_scope', ''),
476 ('ldap_attr_login', ''), ('ldap_attr_firstname', ''),
477 ('ldap_attr_login', ''), ('ldap_attr_firstname', ''),
477 ('ldap_attr_lastname', ''), ('ldap_attr_email', '')]:
478 ('ldap_attr_lastname', ''), ('ldap_attr_email', '')]:
478
479
479 if skip_existing and RhodeCodeSetting.get_by_name(k) != None:
480 if skip_existing and RhodeCodeSetting.get_by_name(k) != None:
480 log.debug('Skipping option %s' % k)
481 log.debug('Skipping option %s' % k)
481 continue
482 continue
482 setting = RhodeCodeSetting(k, v)
483 setting = RhodeCodeSetting(k, v)
483 self.sa.add(setting)
484 self.sa.add(setting)
484
485
486 def create_default_options(self, skip_existing=False):
487 """Creates default settings"""
488
489 for k, v in [
490 ('default_repo_enable_locking', False),
491 ('default_repo_enable_downloads', False),
492 ('default_repo_enable_statistics', False),
493 ('default_repo_private', False),
494 ('default_repo_type', 'hg')]:
495
496 if skip_existing and RhodeCodeSetting.get_by_name(k) != None:
497 log.debug('Skipping option %s' % k)
498 continue
499 setting = RhodeCodeSetting(k, v)
500 self.sa.add(setting)
501
485 def fixup_groups(self):
502 def fixup_groups(self):
486 def_usr = User.get_by_username('default')
503 def_usr = User.get_by_username('default')
487 for g in RepoGroup.query().all():
504 for g in RepoGroup.query().all():
488 g.group_name = g.get_new_name(g.name)
505 g.group_name = g.get_new_name(g.name)
489 self.sa.add(g)
506 self.sa.add(g)
490 # get default perm
507 # get default perm
491 default = UserRepoGroupToPerm.query()\
508 default = UserRepoGroupToPerm.query()\
492 .filter(UserRepoGroupToPerm.group == g)\
509 .filter(UserRepoGroupToPerm.group == g)\
493 .filter(UserRepoGroupToPerm.user == def_usr)\
510 .filter(UserRepoGroupToPerm.user == def_usr)\
494 .scalar()
511 .scalar()
495
512
496 if default is None:
513 if default is None:
497 log.debug('missing default permission for group %s adding' % g)
514 log.debug('missing default permission for group %s adding' % g)
498 ReposGroupModel()._create_default_perms(g)
515 ReposGroupModel()._create_default_perms(g)
499
516
500 def reset_permissions(self, username):
517 def reset_permissions(self, username):
501 """
518 """
502 Resets permissions to default state, usefull when old systems had
519 Resets permissions to default state, usefull when old systems had
503 bad permissions, we must clean them up
520 bad permissions, we must clean them up
504
521
505 :param username:
522 :param username:
506 :type username:
523 :type username:
507 """
524 """
508 default_user = User.get_by_username(username)
525 default_user = User.get_by_username(username)
509 if not default_user:
526 if not default_user:
510 return
527 return
511
528
512 u2p = UserToPerm.query()\
529 u2p = UserToPerm.query()\
513 .filter(UserToPerm.user == default_user).all()
530 .filter(UserToPerm.user == default_user).all()
514 fixed = False
531 fixed = False
515 if len(u2p) != len(User.DEFAULT_PERMISSIONS):
532 if len(u2p) != len(User.DEFAULT_PERMISSIONS):
516 for p in u2p:
533 for p in u2p:
517 Session().delete(p)
534 Session().delete(p)
518 fixed = True
535 fixed = True
519 self.populate_default_permissions()
536 self.populate_default_permissions()
520 return fixed
537 return fixed
521
538
522 def config_prompt(self, test_repo_path='', retries=3):
539 def config_prompt(self, test_repo_path='', retries=3):
523 defaults = self.cli_args
540 defaults = self.cli_args
524 _path = defaults.get('repos_location')
541 _path = defaults.get('repos_location')
525 if retries == 3:
542 if retries == 3:
526 log.info('Setting up repositories config')
543 log.info('Setting up repositories config')
527
544
528 if _path is not None:
545 if _path is not None:
529 path = _path
546 path = _path
530 elif not self.tests and not test_repo_path:
547 elif not self.tests and not test_repo_path:
531 path = raw_input(
548 path = raw_input(
532 'Enter a valid absolute path to store repositories. '
549 'Enter a valid absolute path to store repositories. '
533 'All repositories in that path will be added automatically:'
550 'All repositories in that path will be added automatically:'
534 )
551 )
535 else:
552 else:
536 path = test_repo_path
553 path = test_repo_path
537 path_ok = True
554 path_ok = True
538
555
539 # check proper dir
556 # check proper dir
540 if not os.path.isdir(path):
557 if not os.path.isdir(path):
541 path_ok = False
558 path_ok = False
542 log.error('Given path %s is not a valid directory' % path)
559 log.error('Given path %s is not a valid directory' % path)
543
560
544 elif not os.path.isabs(path):
561 elif not os.path.isabs(path):
545 path_ok = False
562 path_ok = False
546 log.error('Given path %s is not an absolute path' % path)
563 log.error('Given path %s is not an absolute path' % path)
547
564
548 # check write access
565 # check write access
549 elif not os.access(path, os.W_OK) and path_ok:
566 elif not os.access(path, os.W_OK) and path_ok:
550 path_ok = False
567 path_ok = False
551 log.error('No write permission to given path %s' % path)
568 log.error('No write permission to given path %s' % path)
552
569
553 if retries == 0:
570 if retries == 0:
554 sys.exit('max retries reached')
571 sys.exit('max retries reached')
555 if path_ok is False:
572 if path_ok is False:
556 retries -= 1
573 retries -= 1
557 return self.config_prompt(test_repo_path, retries)
574 return self.config_prompt(test_repo_path, retries)
558
575
559 real_path = os.path.normpath(os.path.realpath(path))
576 real_path = os.path.normpath(os.path.realpath(path))
560
577
561 if real_path != os.path.normpath(path):
578 if real_path != os.path.normpath(path):
562 if not ask_ok(('Path looks like a symlink, Rhodecode will store '
579 if not ask_ok(('Path looks like a symlink, Rhodecode will store '
563 'given path as %s ? [y/n]') % (real_path)):
580 'given path as %s ? [y/n]') % (real_path)):
564 log.error('Canceled by user')
581 log.error('Canceled by user')
565 sys.exit(-1)
582 sys.exit(-1)
566
583
567 return real_path
584 return real_path
568
585
569 def create_settings(self, path):
586 def create_settings(self, path):
570
587
571 self.create_ui_settings()
588 self.create_ui_settings()
572
589
573 #HG UI OPTIONS
590 #HG UI OPTIONS
574 web1 = RhodeCodeUi()
591 web1 = RhodeCodeUi()
575 web1.ui_section = 'web'
592 web1.ui_section = 'web'
576 web1.ui_key = 'push_ssl'
593 web1.ui_key = 'push_ssl'
577 web1.ui_value = 'false'
594 web1.ui_value = 'false'
578
595
579 web2 = RhodeCodeUi()
596 web2 = RhodeCodeUi()
580 web2.ui_section = 'web'
597 web2.ui_section = 'web'
581 web2.ui_key = 'allow_archive'
598 web2.ui_key = 'allow_archive'
582 web2.ui_value = 'gz zip bz2'
599 web2.ui_value = 'gz zip bz2'
583
600
584 web3 = RhodeCodeUi()
601 web3 = RhodeCodeUi()
585 web3.ui_section = 'web'
602 web3.ui_section = 'web'
586 web3.ui_key = 'allow_push'
603 web3.ui_key = 'allow_push'
587 web3.ui_value = '*'
604 web3.ui_value = '*'
588
605
589 web4 = RhodeCodeUi()
606 web4 = RhodeCodeUi()
590 web4.ui_section = 'web'
607 web4.ui_section = 'web'
591 web4.ui_key = 'baseurl'
608 web4.ui_key = 'baseurl'
592 web4.ui_value = '/'
609 web4.ui_value = '/'
593
610
594 paths = RhodeCodeUi()
611 paths = RhodeCodeUi()
595 paths.ui_section = 'paths'
612 paths.ui_section = 'paths'
596 paths.ui_key = '/'
613 paths.ui_key = '/'
597 paths.ui_value = path
614 paths.ui_value = path
598
615
599 phases = RhodeCodeUi()
616 phases = RhodeCodeUi()
600 phases.ui_section = 'phases'
617 phases.ui_section = 'phases'
601 phases.ui_key = 'publish'
618 phases.ui_key = 'publish'
602 phases.ui_value = False
619 phases.ui_value = False
603
620
604 sett1 = RhodeCodeSetting('realm', 'RhodeCode authentication')
621 sett1 = RhodeCodeSetting('realm', 'RhodeCode authentication')
605 sett2 = RhodeCodeSetting('title', 'RhodeCode')
622 sett2 = RhodeCodeSetting('title', 'RhodeCode')
606 sett3 = RhodeCodeSetting('ga_code', '')
623 sett3 = RhodeCodeSetting('ga_code', '')
607
624
608 sett4 = RhodeCodeSetting('show_public_icon', True)
625 sett4 = RhodeCodeSetting('show_public_icon', True)
609 sett5 = RhodeCodeSetting('show_private_icon', True)
626 sett5 = RhodeCodeSetting('show_private_icon', True)
610 sett6 = RhodeCodeSetting('stylify_metatags', False)
627 sett6 = RhodeCodeSetting('stylify_metatags', False)
611
628
612 self.sa.add(web1)
629 self.sa.add(web1)
613 self.sa.add(web2)
630 self.sa.add(web2)
614 self.sa.add(web3)
631 self.sa.add(web3)
615 self.sa.add(web4)
632 self.sa.add(web4)
616 self.sa.add(paths)
633 self.sa.add(paths)
617 self.sa.add(sett1)
634 self.sa.add(sett1)
618 self.sa.add(sett2)
635 self.sa.add(sett2)
619 self.sa.add(sett3)
636 self.sa.add(sett3)
620 self.sa.add(sett4)
637 self.sa.add(sett4)
621 self.sa.add(sett5)
638 self.sa.add(sett5)
622 self.sa.add(sett6)
639 self.sa.add(sett6)
623
640
624 self.create_ldap_options()
641 self.create_ldap_options()
642 self.create_default_options()
625
643
626 log.info('created ui config')
644 log.info('created ui config')
627
645
628 def create_user(self, username, password, email='', admin=False):
646 def create_user(self, username, password, email='', admin=False):
629 log.info('creating user %s' % username)
647 log.info('creating user %s' % username)
630 UserModel().create_or_update(username, password, email,
648 UserModel().create_or_update(username, password, email,
631 firstname='RhodeCode', lastname='Admin',
649 firstname='RhodeCode', lastname='Admin',
632 active=True, admin=admin)
650 active=True, admin=admin)
633
651
634 def create_default_user(self):
652 def create_default_user(self):
635 log.info('creating default user')
653 log.info('creating default user')
636 # create default user for handling default permissions.
654 # create default user for handling default permissions.
637 UserModel().create_or_update(username='default',
655 UserModel().create_or_update(username='default',
638 password=str(uuid.uuid1())[:8],
656 password=str(uuid.uuid1())[:8],
639 email='anonymous@rhodecode.org',
657 email='anonymous@rhodecode.org',
640 firstname='Anonymous', lastname='User')
658 firstname='Anonymous', lastname='User')
641
659
642 def create_permissions(self):
660 def create_permissions(self):
643 # module.(access|create|change|delete)_[name]
661 # module.(access|create|change|delete)_[name]
644 # module.(none|read|write|admin)
662 # module.(none|read|write|admin)
645
663
646 for p in Permission.PERMS:
664 for p in Permission.PERMS:
647 if not Permission.get_by_key(p[0]):
665 if not Permission.get_by_key(p[0]):
648 new_perm = Permission()
666 new_perm = Permission()
649 new_perm.permission_name = p[0]
667 new_perm.permission_name = p[0]
650 new_perm.permission_longname = p[0]
668 new_perm.permission_longname = p[0]
651 self.sa.add(new_perm)
669 self.sa.add(new_perm)
652
670
653 def populate_default_permissions(self):
671 def populate_default_permissions(self):
654 log.info('creating default user permissions')
672 log.info('creating default user permissions')
655
673
656 default_user = User.get_by_username('default')
674 default_user = User.get_by_username('default')
657
675
658 for def_perm in User.DEFAULT_PERMISSIONS:
676 for def_perm in User.DEFAULT_PERMISSIONS:
659
677
660 perm = self.sa.query(Permission)\
678 perm = self.sa.query(Permission)\
661 .filter(Permission.permission_name == def_perm)\
679 .filter(Permission.permission_name == def_perm)\
662 .scalar()
680 .scalar()
663 if not perm:
681 if not perm:
664 raise Exception(
682 raise Exception(
665 'CRITICAL: permission %s not found inside database !!'
683 'CRITICAL: permission %s not found inside database !!'
666 % def_perm
684 % def_perm
667 )
685 )
668 if not UserToPerm.query()\
686 if not UserToPerm.query()\
669 .filter(UserToPerm.permission == perm)\
687 .filter(UserToPerm.permission == perm)\
670 .filter(UserToPerm.user == default_user).scalar():
688 .filter(UserToPerm.user == default_user).scalar():
671 reg_perm = UserToPerm()
689 reg_perm = UserToPerm()
672 reg_perm.user = default_user
690 reg_perm.user = default_user
673 reg_perm.permission = perm
691 reg_perm.permission = perm
674 self.sa.add(reg_perm)
692 self.sa.add(reg_perm)
675
693
676 def finish(self):
694 def finish(self):
677 """
695 """
678 Function executed at the end of setup
696 Function executed at the end of setup
679 """
697 """
680 if not __py_version__ >= (2, 6):
698 if not __py_version__ >= (2, 6):
681 notify('Python2.5 detected, please switch '
699 notify('Python2.5 detected, please switch '
682 'egg:waitress#main -> egg:Paste#http '
700 'egg:waitress#main -> egg:Paste#http '
683 'in your .ini file')
701 'in your .ini file')
@@ -1,28 +1,29 b''
1 import logging
1 import logging
2 import datetime
2 import datetime
3
3
4 from sqlalchemy import *
4 from sqlalchemy import *
5 from sqlalchemy.exc import DatabaseError
5 from sqlalchemy.exc import DatabaseError
6 from sqlalchemy.orm import relation, backref, class_mapper
6 from sqlalchemy.orm import relation, backref, class_mapper
7 from sqlalchemy.orm.session import Session
7 from sqlalchemy.orm.session import Session
8 from sqlalchemy.ext.declarative import declarative_base
8 from sqlalchemy.ext.declarative import declarative_base
9
9
10 from rhodecode.lib.dbmigrate.migrate import *
10 from rhodecode.lib.dbmigrate.migrate import *
11 from rhodecode.lib.dbmigrate.migrate.changeset import *
11 from rhodecode.lib.dbmigrate.migrate.changeset import *
12
12
13 from rhodecode.model.meta import Base
13 from rhodecode.model.meta import Base
14 from rhodecode.model import meta
14 from rhodecode.model import meta
15
15
16 log = logging.getLogger(__name__)
16 log = logging.getLogger(__name__)
17
17
18
18
19 def upgrade(migrate_engine):
19 def upgrade(migrate_engine):
20 """
20 """
21 Upgrade operations go here.
21 Upgrade operations go here.
22 Don't create your own engine; bind migrate_engine to your metadata
22 Don't create your own engine; bind migrate_engine to your metadata
23 """
23 """
24 pass
24 pass
25
25
26
26 def downgrade(migrate_engine):
27 def downgrade(migrate_engine):
27 meta = MetaData()
28 meta = MetaData()
28 meta.bind = migrate_engine
29 meta.bind = migrate_engine
@@ -1,569 +1,569 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 import time
27 import time
28 import datetime
28 import datetime
29 import webob
29 import webob
30
30
31 from pylons.i18n.translation import _, ungettext
31 from pylons.i18n.translation import _, ungettext
32 from rhodecode.lib.vcs.utils.lazy import LazyProperty
32 from rhodecode.lib.vcs.utils.lazy import LazyProperty
33
33
34
34
35 def __get_lem():
35 def __get_lem():
36 """
36 """
37 Get language extension map based on what's inside pygments lexers
37 Get language extension map based on what's inside pygments lexers
38 """
38 """
39 from pygments import lexers
39 from pygments import lexers
40 from string import lower
40 from string import lower
41 from collections import defaultdict
41 from collections import defaultdict
42
42
43 d = defaultdict(lambda: [])
43 d = defaultdict(lambda: [])
44
44
45 def __clean(s):
45 def __clean(s):
46 s = s.lstrip('*')
46 s = s.lstrip('*')
47 s = s.lstrip('.')
47 s = s.lstrip('.')
48
48
49 if s.find('[') != -1:
49 if s.find('[') != -1:
50 exts = []
50 exts = []
51 start, stop = s.find('['), s.find(']')
51 start, stop = s.find('['), s.find(']')
52
52
53 for suffix in s[start + 1:stop]:
53 for suffix in s[start + 1:stop]:
54 exts.append(s[:s.find('[')] + suffix)
54 exts.append(s[:s.find('[')] + suffix)
55 return map(lower, exts)
55 return map(lower, exts)
56 else:
56 else:
57 return map(lower, [s])
57 return map(lower, [s])
58
58
59 for lx, t in sorted(lexers.LEXERS.items()):
59 for lx, t in sorted(lexers.LEXERS.items()):
60 m = map(__clean, t[-2])
60 m = map(__clean, t[-2])
61 if m:
61 if m:
62 m = reduce(lambda x, y: x + y, m)
62 m = reduce(lambda x, y: x + y, m)
63 for ext in m:
63 for ext in m:
64 desc = lx.replace('Lexer', '')
64 desc = lx.replace('Lexer', '')
65 d[ext].append(desc)
65 d[ext].append(desc)
66
66
67 return dict(d)
67 return dict(d)
68
68
69
69
70 def str2bool(_str):
70 def str2bool(_str):
71 """
71 """
72 returs True/False value from given string, it tries to translate the
72 returs True/False value from given string, it tries to translate the
73 string into boolean
73 string into boolean
74
74
75 :param _str: string value to translate into boolean
75 :param _str: string value to translate into boolean
76 :rtype: boolean
76 :rtype: boolean
77 :returns: boolean from given string
77 :returns: boolean from given string
78 """
78 """
79 if _str is None:
79 if _str is None:
80 return False
80 return False
81 if _str in (True, False):
81 if _str in (True, False):
82 return _str
82 return _str
83 _str = str(_str).strip().lower()
83 _str = str(_str).strip().lower()
84 return _str in ('t', 'true', 'y', 'yes', 'on', '1')
84 return _str in ('t', 'true', 'y', 'yes', 'on', '1')
85
85
86
86
87 def aslist(obj, sep=None, strip=True):
87 def aslist(obj, sep=None, strip=True):
88 """
88 """
89 Returns given string separated by sep as list
89 Returns given string separated by sep as list
90
90
91 :param obj:
91 :param obj:
92 :param sep:
92 :param sep:
93 :param strip:
93 :param strip:
94 """
94 """
95 if isinstance(obj, (basestring)):
95 if isinstance(obj, (basestring)):
96 lst = obj.split(sep)
96 lst = obj.split(sep)
97 if strip:
97 if strip:
98 lst = [v.strip() for v in lst]
98 lst = [v.strip() for v in lst]
99 return lst
99 return lst
100 elif isinstance(obj, (list, tuple)):
100 elif isinstance(obj, (list, tuple)):
101 return obj
101 return obj
102 elif obj is None:
102 elif obj is None:
103 return []
103 return []
104 else:
104 else:
105 return [obj]
105 return [obj]
106
106
107
107
108 def convert_line_endings(line, mode):
108 def convert_line_endings(line, mode):
109 """
109 """
110 Converts a given line "line end" accordingly to given mode
110 Converts a given line "line end" accordingly to given mode
111
111
112 Available modes are::
112 Available modes are::
113 0 - Unix
113 0 - Unix
114 1 - Mac
114 1 - Mac
115 2 - DOS
115 2 - DOS
116
116
117 :param line: given line to convert
117 :param line: given line to convert
118 :param mode: mode to convert to
118 :param mode: mode to convert to
119 :rtype: str
119 :rtype: str
120 :return: converted line according to mode
120 :return: converted line according to mode
121 """
121 """
122 from string import replace
122 from string import replace
123
123
124 if mode == 0:
124 if mode == 0:
125 line = replace(line, '\r\n', '\n')
125 line = replace(line, '\r\n', '\n')
126 line = replace(line, '\r', '\n')
126 line = replace(line, '\r', '\n')
127 elif mode == 1:
127 elif mode == 1:
128 line = replace(line, '\r\n', '\r')
128 line = replace(line, '\r\n', '\r')
129 line = replace(line, '\n', '\r')
129 line = replace(line, '\n', '\r')
130 elif mode == 2:
130 elif mode == 2:
131 line = re.sub("\r(?!\n)|(?<!\r)\n", "\r\n", line)
131 line = re.sub("\r(?!\n)|(?<!\r)\n", "\r\n", line)
132 return line
132 return line
133
133
134
134
135 def detect_mode(line, default):
135 def detect_mode(line, default):
136 """
136 """
137 Detects line break for given line, if line break couldn't be found
137 Detects line break for given line, if line break couldn't be found
138 given default value is returned
138 given default value is returned
139
139
140 :param line: str line
140 :param line: str line
141 :param default: default
141 :param default: default
142 :rtype: int
142 :rtype: int
143 :return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS
143 :return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS
144 """
144 """
145 if line.endswith('\r\n'):
145 if line.endswith('\r\n'):
146 return 2
146 return 2
147 elif line.endswith('\n'):
147 elif line.endswith('\n'):
148 return 0
148 return 0
149 elif line.endswith('\r'):
149 elif line.endswith('\r'):
150 return 1
150 return 1
151 else:
151 else:
152 return default
152 return default
153
153
154
154
155 def generate_api_key(username, salt=None):
155 def generate_api_key(username, salt=None):
156 """
156 """
157 Generates unique API key for given username, if salt is not given
157 Generates unique API key for given username, if salt is not given
158 it'll be generated from some random string
158 it'll be generated from some random string
159
159
160 :param username: username as string
160 :param username: username as string
161 :param salt: salt to hash generate KEY
161 :param salt: salt to hash generate KEY
162 :rtype: str
162 :rtype: str
163 :returns: sha1 hash from username+salt
163 :returns: sha1 hash from username+salt
164 """
164 """
165 from tempfile import _RandomNameSequence
165 from tempfile import _RandomNameSequence
166 import hashlib
166 import hashlib
167
167
168 if salt is None:
168 if salt is None:
169 salt = _RandomNameSequence().next()
169 salt = _RandomNameSequence().next()
170
170
171 return hashlib.sha1(username + salt).hexdigest()
171 return hashlib.sha1(username + salt).hexdigest()
172
172
173
173
174 def safe_int(val, default=None):
174 def safe_int(val, default=None):
175 """
175 """
176 Returns int() of val if val is not convertable to int use default
176 Returns int() of val if val is not convertable to int use default
177 instead
177 instead
178
178
179 :param val:
179 :param val:
180 :param default:
180 :param default:
181 """
181 """
182
182
183 try:
183 try:
184 val = int(val)
184 val = int(val)
185 except ValueError:
185 except ValueError:
186 val = default
186 val = default
187
187
188 return val
188 return val
189
189
190
190
191 def safe_unicode(str_, from_encoding=None):
191 def safe_unicode(str_, from_encoding=None):
192 """
192 """
193 safe unicode function. Does few trick to turn str_ into unicode
193 safe unicode function. Does few trick to turn str_ into unicode
194
194
195 In case of UnicodeDecode error we try to return it with encoding detected
195 In case of UnicodeDecode error we try to return it with encoding detected
196 by chardet library if it fails fallback to unicode with errors replaced
196 by chardet library if it fails fallback to unicode with errors replaced
197
197
198 :param str_: string to decode
198 :param str_: string to decode
199 :rtype: unicode
199 :rtype: unicode
200 :returns: unicode object
200 :returns: unicode object
201 """
201 """
202 if isinstance(str_, unicode):
202 if isinstance(str_, unicode):
203 return str_
203 return str_
204
204
205 if not from_encoding:
205 if not from_encoding:
206 import rhodecode
206 import rhodecode
207 DEFAULT_ENCODINGS = aslist(rhodecode.CONFIG.get('default_encoding',
207 DEFAULT_ENCODINGS = aslist(rhodecode.CONFIG.get('default_encoding',
208 'utf8'), sep=',')
208 'utf8'), sep=',')
209 from_encoding = DEFAULT_ENCODINGS
209 from_encoding = DEFAULT_ENCODINGS
210
210
211 if not isinstance(from_encoding, (list, tuple)):
211 if not isinstance(from_encoding, (list, tuple)):
212 from_encoding = [from_encoding]
212 from_encoding = [from_encoding]
213
213
214 try:
214 try:
215 return unicode(str_)
215 return unicode(str_)
216 except UnicodeDecodeError:
216 except UnicodeDecodeError:
217 pass
217 pass
218
218
219 for enc in from_encoding:
219 for enc in from_encoding:
220 try:
220 try:
221 return unicode(str_, enc)
221 return unicode(str_, enc)
222 except UnicodeDecodeError:
222 except UnicodeDecodeError:
223 pass
223 pass
224
224
225 try:
225 try:
226 import chardet
226 import chardet
227 encoding = chardet.detect(str_)['encoding']
227 encoding = chardet.detect(str_)['encoding']
228 if encoding is None:
228 if encoding is None:
229 raise Exception()
229 raise Exception()
230 return str_.decode(encoding)
230 return str_.decode(encoding)
231 except (ImportError, UnicodeDecodeError, Exception):
231 except (ImportError, UnicodeDecodeError, Exception):
232 return unicode(str_, from_encoding[0], 'replace')
232 return unicode(str_, from_encoding[0], 'replace')
233
233
234
234
235 def safe_str(unicode_, to_encoding=None):
235 def safe_str(unicode_, to_encoding=None):
236 """
236 """
237 safe str function. Does few trick to turn unicode_ into string
237 safe str function. Does few trick to turn unicode_ into string
238
238
239 In case of UnicodeEncodeError we try to return it with encoding detected
239 In case of UnicodeEncodeError we try to return it with encoding detected
240 by chardet library if it fails fallback to string with errors replaced
240 by chardet library if it fails fallback to string with errors replaced
241
241
242 :param unicode_: unicode to encode
242 :param unicode_: unicode to encode
243 :rtype: str
243 :rtype: str
244 :returns: str object
244 :returns: str object
245 """
245 """
246
246
247 # if it's not basestr cast to str
247 # if it's not basestr cast to str
248 if not isinstance(unicode_, basestring):
248 if not isinstance(unicode_, basestring):
249 return str(unicode_)
249 return str(unicode_)
250
250
251 if isinstance(unicode_, str):
251 if isinstance(unicode_, str):
252 return unicode_
252 return unicode_
253
253
254 if not to_encoding:
254 if not to_encoding:
255 import rhodecode
255 import rhodecode
256 DEFAULT_ENCODINGS = aslist(rhodecode.CONFIG.get('default_encoding',
256 DEFAULT_ENCODINGS = aslist(rhodecode.CONFIG.get('default_encoding',
257 'utf8'), sep=',')
257 'utf8'), sep=',')
258 to_encoding = DEFAULT_ENCODINGS
258 to_encoding = DEFAULT_ENCODINGS
259
259
260 if not isinstance(to_encoding, (list, tuple)):
260 if not isinstance(to_encoding, (list, tuple)):
261 to_encoding = [to_encoding]
261 to_encoding = [to_encoding]
262
262
263 for enc in to_encoding:
263 for enc in to_encoding:
264 try:
264 try:
265 return unicode_.encode(enc)
265 return unicode_.encode(enc)
266 except UnicodeEncodeError:
266 except UnicodeEncodeError:
267 pass
267 pass
268
268
269 try:
269 try:
270 import chardet
270 import chardet
271 encoding = chardet.detect(unicode_)['encoding']
271 encoding = chardet.detect(unicode_)['encoding']
272 if encoding is None:
272 if encoding is None:
273 raise UnicodeEncodeError()
273 raise UnicodeEncodeError()
274
274
275 return unicode_.encode(encoding)
275 return unicode_.encode(encoding)
276 except (ImportError, UnicodeEncodeError):
276 except (ImportError, UnicodeEncodeError):
277 return unicode_.encode(to_encoding[0], 'replace')
277 return unicode_.encode(to_encoding[0], 'replace')
278
278
279 return safe_str
279 return safe_str
280
280
281
281
282 def remove_suffix(s, suffix):
282 def remove_suffix(s, suffix):
283 if s.endswith(suffix):
283 if s.endswith(suffix):
284 s = s[:-1 * len(suffix)]
284 s = s[:-1 * len(suffix)]
285 return s
285 return s
286
286
287
287
288 def remove_prefix(s, prefix):
288 def remove_prefix(s, prefix):
289 if s.startswith(prefix):
289 if s.startswith(prefix):
290 s = s[:-1 * len(prefix)]
290 s = s[len(prefix):]
291 return s
291 return s
292
292
293
293
294 def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
294 def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
295 """
295 """
296 Custom engine_from_config functions that makes sure we use NullPool for
296 Custom engine_from_config functions that makes sure we use NullPool for
297 file based sqlite databases. This prevents errors on sqlite. This only
297 file based sqlite databases. This prevents errors on sqlite. This only
298 applies to sqlalchemy versions < 0.7.0
298 applies to sqlalchemy versions < 0.7.0
299
299
300 """
300 """
301 import sqlalchemy
301 import sqlalchemy
302 from sqlalchemy import engine_from_config as efc
302 from sqlalchemy import engine_from_config as efc
303 import logging
303 import logging
304
304
305 if int(sqlalchemy.__version__.split('.')[1]) < 7:
305 if int(sqlalchemy.__version__.split('.')[1]) < 7:
306
306
307 # This solution should work for sqlalchemy < 0.7.0, and should use
307 # This solution should work for sqlalchemy < 0.7.0, and should use
308 # proxy=TimerProxy() for execution time profiling
308 # proxy=TimerProxy() for execution time profiling
309
309
310 from sqlalchemy.pool import NullPool
310 from sqlalchemy.pool import NullPool
311 url = configuration[prefix + 'url']
311 url = configuration[prefix + 'url']
312
312
313 if url.startswith('sqlite'):
313 if url.startswith('sqlite'):
314 kwargs.update({'poolclass': NullPool})
314 kwargs.update({'poolclass': NullPool})
315 return efc(configuration, prefix, **kwargs)
315 return efc(configuration, prefix, **kwargs)
316 else:
316 else:
317 import time
317 import time
318 from sqlalchemy import event
318 from sqlalchemy import event
319 from sqlalchemy.engine import Engine
319 from sqlalchemy.engine import Engine
320
320
321 log = logging.getLogger('sqlalchemy.engine')
321 log = logging.getLogger('sqlalchemy.engine')
322 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38)
322 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38)
323 engine = efc(configuration, prefix, **kwargs)
323 engine = efc(configuration, prefix, **kwargs)
324
324
325 def color_sql(sql):
325 def color_sql(sql):
326 COLOR_SEQ = "\033[1;%dm"
326 COLOR_SEQ = "\033[1;%dm"
327 COLOR_SQL = YELLOW
327 COLOR_SQL = YELLOW
328 normal = '\x1b[0m'
328 normal = '\x1b[0m'
329 return ''.join([COLOR_SEQ % COLOR_SQL, sql, normal])
329 return ''.join([COLOR_SEQ % COLOR_SQL, sql, normal])
330
330
331 if configuration['debug']:
331 if configuration['debug']:
332 #attach events only for debug configuration
332 #attach events only for debug configuration
333
333
334 def before_cursor_execute(conn, cursor, statement,
334 def before_cursor_execute(conn, cursor, statement,
335 parameters, context, executemany):
335 parameters, context, executemany):
336 context._query_start_time = time.time()
336 context._query_start_time = time.time()
337 log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
337 log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
338
338
339 def after_cursor_execute(conn, cursor, statement,
339 def after_cursor_execute(conn, cursor, statement,
340 parameters, context, executemany):
340 parameters, context, executemany):
341 total = time.time() - context._query_start_time
341 total = time.time() - context._query_start_time
342 log.info(color_sql("<<<<< TOTAL TIME: %f <<<<<" % total))
342 log.info(color_sql("<<<<< TOTAL TIME: %f <<<<<" % total))
343
343
344 event.listen(engine, "before_cursor_execute",
344 event.listen(engine, "before_cursor_execute",
345 before_cursor_execute)
345 before_cursor_execute)
346 event.listen(engine, "after_cursor_execute",
346 event.listen(engine, "after_cursor_execute",
347 after_cursor_execute)
347 after_cursor_execute)
348
348
349 return engine
349 return engine
350
350
351
351
352 def age(prevdate):
352 def age(prevdate):
353 """
353 """
354 turns a datetime into an age string.
354 turns a datetime into an age string.
355
355
356 :param prevdate: datetime object
356 :param prevdate: datetime object
357 :rtype: unicode
357 :rtype: unicode
358 :returns: unicode words describing age
358 :returns: unicode words describing age
359 """
359 """
360
360
361 order = ['year', 'month', 'day', 'hour', 'minute', 'second']
361 order = ['year', 'month', 'day', 'hour', 'minute', 'second']
362 deltas = {}
362 deltas = {}
363 future = False
363 future = False
364
364
365 # Get date parts deltas
365 # Get date parts deltas
366 now = datetime.datetime.now()
366 now = datetime.datetime.now()
367 if prevdate > now:
367 if prevdate > now:
368 now, prevdate = prevdate, now
368 now, prevdate = prevdate, now
369 future = True
369 future = True
370
370
371 for part in order:
371 for part in order:
372 deltas[part] = getattr(now, part) - getattr(prevdate, part)
372 deltas[part] = getattr(now, part) - getattr(prevdate, part)
373
373
374 # Fix negative offsets (there is 1 second between 10:59:59 and 11:00:00,
374 # Fix negative offsets (there is 1 second between 10:59:59 and 11:00:00,
375 # not 1 hour, -59 minutes and -59 seconds)
375 # not 1 hour, -59 minutes and -59 seconds)
376
376
377 for num, length in [(5, 60), (4, 60), (3, 24)]: # seconds, minutes, hours
377 for num, length in [(5, 60), (4, 60), (3, 24)]: # seconds, minutes, hours
378 part = order[num]
378 part = order[num]
379 carry_part = order[num - 1]
379 carry_part = order[num - 1]
380
380
381 if deltas[part] < 0:
381 if deltas[part] < 0:
382 deltas[part] += length
382 deltas[part] += length
383 deltas[carry_part] -= 1
383 deltas[carry_part] -= 1
384
384
385 # Same thing for days except that the increment depends on the (variable)
385 # Same thing for days except that the increment depends on the (variable)
386 # number of days in the month
386 # number of days in the month
387 month_lengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
387 month_lengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
388 if deltas['day'] < 0:
388 if deltas['day'] < 0:
389 if prevdate.month == 2 and (prevdate.year % 4 == 0 and
389 if prevdate.month == 2 and (prevdate.year % 4 == 0 and
390 (prevdate.year % 100 != 0 or prevdate.year % 400 == 0)):
390 (prevdate.year % 100 != 0 or prevdate.year % 400 == 0)):
391 deltas['day'] += 29
391 deltas['day'] += 29
392 else:
392 else:
393 deltas['day'] += month_lengths[prevdate.month - 1]
393 deltas['day'] += month_lengths[prevdate.month - 1]
394
394
395 deltas['month'] -= 1
395 deltas['month'] -= 1
396
396
397 if deltas['month'] < 0:
397 if deltas['month'] < 0:
398 deltas['month'] += 12
398 deltas['month'] += 12
399 deltas['year'] -= 1
399 deltas['year'] -= 1
400
400
401 # Format the result
401 # Format the result
402 fmt_funcs = {
402 fmt_funcs = {
403 'year': lambda d: ungettext(u'%d year', '%d years', d) % d,
403 'year': lambda d: ungettext(u'%d year', '%d years', d) % d,
404 'month': lambda d: ungettext(u'%d month', '%d months', d) % d,
404 'month': lambda d: ungettext(u'%d month', '%d months', d) % d,
405 'day': lambda d: ungettext(u'%d day', '%d days', d) % d,
405 'day': lambda d: ungettext(u'%d day', '%d days', d) % d,
406 'hour': lambda d: ungettext(u'%d hour', '%d hours', d) % d,
406 'hour': lambda d: ungettext(u'%d hour', '%d hours', d) % d,
407 'minute': lambda d: ungettext(u'%d minute', '%d minutes', d) % d,
407 'minute': lambda d: ungettext(u'%d minute', '%d minutes', d) % d,
408 'second': lambda d: ungettext(u'%d second', '%d seconds', d) % d,
408 'second': lambda d: ungettext(u'%d second', '%d seconds', d) % d,
409 }
409 }
410
410
411 for i, part in enumerate(order):
411 for i, part in enumerate(order):
412 value = deltas[part]
412 value = deltas[part]
413 if value == 0:
413 if value == 0:
414 continue
414 continue
415
415
416 if i < 5:
416 if i < 5:
417 sub_part = order[i + 1]
417 sub_part = order[i + 1]
418 sub_value = deltas[sub_part]
418 sub_value = deltas[sub_part]
419 else:
419 else:
420 sub_value = 0
420 sub_value = 0
421
421
422 if sub_value == 0:
422 if sub_value == 0:
423 if future:
423 if future:
424 return _(u'in %s') % fmt_funcs[part](value)
424 return _(u'in %s') % fmt_funcs[part](value)
425 else:
425 else:
426 return _(u'%s ago') % fmt_funcs[part](value)
426 return _(u'%s ago') % fmt_funcs[part](value)
427 if future:
427 if future:
428 return _(u'in %s and %s') % (fmt_funcs[part](value),
428 return _(u'in %s and %s') % (fmt_funcs[part](value),
429 fmt_funcs[sub_part](sub_value))
429 fmt_funcs[sub_part](sub_value))
430 else:
430 else:
431 return _(u'%s and %s ago') % (fmt_funcs[part](value),
431 return _(u'%s and %s ago') % (fmt_funcs[part](value),
432 fmt_funcs[sub_part](sub_value))
432 fmt_funcs[sub_part](sub_value))
433
433
434 return _(u'just now')
434 return _(u'just now')
435
435
436
436
437 def uri_filter(uri):
437 def uri_filter(uri):
438 """
438 """
439 Removes user:password from given url string
439 Removes user:password from given url string
440
440
441 :param uri:
441 :param uri:
442 :rtype: unicode
442 :rtype: unicode
443 :returns: filtered list of strings
443 :returns: filtered list of strings
444 """
444 """
445 if not uri:
445 if not uri:
446 return ''
446 return ''
447
447
448 proto = ''
448 proto = ''
449
449
450 for pat in ('https://', 'http://'):
450 for pat in ('https://', 'http://'):
451 if uri.startswith(pat):
451 if uri.startswith(pat):
452 uri = uri[len(pat):]
452 uri = uri[len(pat):]
453 proto = pat
453 proto = pat
454 break
454 break
455
455
456 # remove passwords and username
456 # remove passwords and username
457 uri = uri[uri.find('@') + 1:]
457 uri = uri[uri.find('@') + 1:]
458
458
459 # get the port
459 # get the port
460 cred_pos = uri.find(':')
460 cred_pos = uri.find(':')
461 if cred_pos == -1:
461 if cred_pos == -1:
462 host, port = uri, None
462 host, port = uri, None
463 else:
463 else:
464 host, port = uri[:cred_pos], uri[cred_pos + 1:]
464 host, port = uri[:cred_pos], uri[cred_pos + 1:]
465
465
466 return filter(None, [proto, host, port])
466 return filter(None, [proto, host, port])
467
467
468
468
469 def credentials_filter(uri):
469 def credentials_filter(uri):
470 """
470 """
471 Returns a url with removed credentials
471 Returns a url with removed credentials
472
472
473 :param uri:
473 :param uri:
474 """
474 """
475
475
476 uri = uri_filter(uri)
476 uri = uri_filter(uri)
477 #check if we have port
477 #check if we have port
478 if len(uri) > 2 and uri[2]:
478 if len(uri) > 2 and uri[2]:
479 uri[2] = ':' + uri[2]
479 uri[2] = ':' + uri[2]
480
480
481 return ''.join(uri)
481 return ''.join(uri)
482
482
483
483
484 def get_changeset_safe(repo, rev):
484 def get_changeset_safe(repo, rev):
485 """
485 """
486 Safe version of get_changeset if this changeset doesn't exists for a
486 Safe version of get_changeset if this changeset doesn't exists for a
487 repo it returns a Dummy one instead
487 repo it returns a Dummy one instead
488
488
489 :param repo:
489 :param repo:
490 :param rev:
490 :param rev:
491 """
491 """
492 from rhodecode.lib.vcs.backends.base import BaseRepository
492 from rhodecode.lib.vcs.backends.base import BaseRepository
493 from rhodecode.lib.vcs.exceptions import RepositoryError
493 from rhodecode.lib.vcs.exceptions import RepositoryError
494 from rhodecode.lib.vcs.backends.base import EmptyChangeset
494 from rhodecode.lib.vcs.backends.base import EmptyChangeset
495 if not isinstance(repo, BaseRepository):
495 if not isinstance(repo, BaseRepository):
496 raise Exception('You must pass an Repository '
496 raise Exception('You must pass an Repository '
497 'object as first argument got %s', type(repo))
497 'object as first argument got %s', type(repo))
498
498
499 try:
499 try:
500 cs = repo.get_changeset(rev)
500 cs = repo.get_changeset(rev)
501 except RepositoryError:
501 except RepositoryError:
502 cs = EmptyChangeset(requested_revision=rev)
502 cs = EmptyChangeset(requested_revision=rev)
503 return cs
503 return cs
504
504
505
505
506 def datetime_to_time(dt):
506 def datetime_to_time(dt):
507 if dt:
507 if dt:
508 return time.mktime(dt.timetuple())
508 return time.mktime(dt.timetuple())
509
509
510
510
511 def time_to_datetime(tm):
511 def time_to_datetime(tm):
512 if tm:
512 if tm:
513 if isinstance(tm, basestring):
513 if isinstance(tm, basestring):
514 try:
514 try:
515 tm = float(tm)
515 tm = float(tm)
516 except ValueError:
516 except ValueError:
517 return
517 return
518 return datetime.datetime.fromtimestamp(tm)
518 return datetime.datetime.fromtimestamp(tm)
519
519
520 MENTIONS_REGEX = r'(?:^@|\s@)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+)(?:\s{1})'
520 MENTIONS_REGEX = r'(?:^@|\s@)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+)(?:\s{1})'
521
521
522
522
523 def extract_mentioned_users(s):
523 def extract_mentioned_users(s):
524 """
524 """
525 Returns unique usernames from given string s that have @mention
525 Returns unique usernames from given string s that have @mention
526
526
527 :param s: string to get mentions
527 :param s: string to get mentions
528 """
528 """
529 usrs = set()
529 usrs = set()
530 for username in re.findall(MENTIONS_REGEX, s):
530 for username in re.findall(MENTIONS_REGEX, s):
531 usrs.add(username)
531 usrs.add(username)
532
532
533 return sorted(list(usrs), key=lambda k: k.lower())
533 return sorted(list(usrs), key=lambda k: k.lower())
534
534
535
535
536 class AttributeDict(dict):
536 class AttributeDict(dict):
537 def __getattr__(self, attr):
537 def __getattr__(self, attr):
538 return self.get(attr, None)
538 return self.get(attr, None)
539 __setattr__ = dict.__setitem__
539 __setattr__ = dict.__setitem__
540 __delattr__ = dict.__delitem__
540 __delattr__ = dict.__delitem__
541
541
542
542
543 def fix_PATH(os_=None):
543 def fix_PATH(os_=None):
544 """
544 """
545 Get current active python path, and append it to PATH variable to fix issues
545 Get current active python path, and append it to PATH variable to fix issues
546 of subprocess calls and different python versions
546 of subprocess calls and different python versions
547 """
547 """
548 import sys
548 import sys
549 if os_ is None:
549 if os_ is None:
550 import os
550 import os
551 else:
551 else:
552 os = os_
552 os = os_
553
553
554 cur_path = os.path.split(sys.executable)[0]
554 cur_path = os.path.split(sys.executable)[0]
555 if not os.environ['PATH'].startswith(cur_path):
555 if not os.environ['PATH'].startswith(cur_path):
556 os.environ['PATH'] = '%s:%s' % (cur_path, os.environ['PATH'])
556 os.environ['PATH'] = '%s:%s' % (cur_path, os.environ['PATH'])
557
557
558
558
559 def obfuscate_url_pw(engine):
559 def obfuscate_url_pw(engine):
560 from sqlalchemy.engine import url
560 from sqlalchemy.engine import url
561 url = url.make_url(engine)
561 url = url.make_url(engine)
562 if url.password:
562 if url.password:
563 url.password = 'XXXXX'
563 url.password = 'XXXXX'
564 return str(url)
564 return str(url)
565
565
566
566
567 def get_server_url(environ):
567 def get_server_url(environ):
568 req = webob.Request(environ)
568 req = webob.Request(environ)
569 return req.host_url + req.script_name
569 return req.host_url + req.script_name
@@ -1,1814 +1,1831 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 import time
31 import time
32 from collections import defaultdict
32 from collections import defaultdict
33
33
34 from sqlalchemy import *
34 from sqlalchemy import *
35 from sqlalchemy.ext.hybrid import hybrid_property
35 from sqlalchemy.ext.hybrid import hybrid_property
36 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
36 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
37 from sqlalchemy.exc import DatabaseError
37 from sqlalchemy.exc import DatabaseError
38 from beaker.cache import cache_region, region_invalidate
38 from beaker.cache import cache_region, region_invalidate
39 from webob.exc import HTTPNotFound
39 from webob.exc import HTTPNotFound
40
40
41 from pylons.i18n.translation import lazy_ugettext as _
41 from pylons.i18n.translation import lazy_ugettext as _
42
42
43 from rhodecode.lib.vcs import get_backend
43 from rhodecode.lib.vcs import get_backend
44 from rhodecode.lib.vcs.utils.helpers import get_scm
44 from rhodecode.lib.vcs.utils.helpers import get_scm
45 from rhodecode.lib.vcs.exceptions import VCSError
45 from rhodecode.lib.vcs.exceptions import VCSError
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
47
47
48 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
48 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
49 safe_unicode, remove_suffix
49 safe_unicode, remove_suffix, remove_prefix
50 from rhodecode.lib.compat import json
50 from rhodecode.lib.compat import json
51 from rhodecode.lib.caching_query import FromCache
51 from rhodecode.lib.caching_query import FromCache
52
52
53 from rhodecode.model.meta import Base, Session
53 from rhodecode.model.meta import Base, Session
54
54
55 URL_SEP = '/'
55 URL_SEP = '/'
56 log = logging.getLogger(__name__)
56 log = logging.getLogger(__name__)
57
57
58 #==============================================================================
58 #==============================================================================
59 # BASE CLASSES
59 # BASE CLASSES
60 #==============================================================================
60 #==============================================================================
61
61
62 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
62 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
63
63
64
64
65 class BaseModel(object):
65 class BaseModel(object):
66 """
66 """
67 Base Model for all classess
67 Base Model for all classess
68 """
68 """
69
69
70 @classmethod
70 @classmethod
71 def _get_keys(cls):
71 def _get_keys(cls):
72 """return column names for this model """
72 """return column names for this model """
73 return class_mapper(cls).c.keys()
73 return class_mapper(cls).c.keys()
74
74
75 def get_dict(self):
75 def get_dict(self):
76 """
76 """
77 return dict with keys and values corresponding
77 return dict with keys and values corresponding
78 to this model data """
78 to this model data """
79
79
80 d = {}
80 d = {}
81 for k in self._get_keys():
81 for k in self._get_keys():
82 d[k] = getattr(self, k)
82 d[k] = getattr(self, k)
83
83
84 # also use __json__() if present to get additional fields
84 # also use __json__() if present to get additional fields
85 _json_attr = getattr(self, '__json__', None)
85 _json_attr = getattr(self, '__json__', None)
86 if _json_attr:
86 if _json_attr:
87 # update with attributes from __json__
87 # update with attributes from __json__
88 if callable(_json_attr):
88 if callable(_json_attr):
89 _json_attr = _json_attr()
89 _json_attr = _json_attr()
90 for k, val in _json_attr.iteritems():
90 for k, val in _json_attr.iteritems():
91 d[k] = val
91 d[k] = val
92 return d
92 return d
93
93
94 def get_appstruct(self):
94 def get_appstruct(self):
95 """return list with keys and values tupples corresponding
95 """return list with keys and values tupples corresponding
96 to this model data """
96 to this model data """
97
97
98 l = []
98 l = []
99 for k in self._get_keys():
99 for k in self._get_keys():
100 l.append((k, getattr(self, k),))
100 l.append((k, getattr(self, k),))
101 return l
101 return l
102
102
103 def populate_obj(self, populate_dict):
103 def populate_obj(self, populate_dict):
104 """populate model with data from given populate_dict"""
104 """populate model with data from given populate_dict"""
105
105
106 for k in self._get_keys():
106 for k in self._get_keys():
107 if k in populate_dict:
107 if k in populate_dict:
108 setattr(self, k, populate_dict[k])
108 setattr(self, k, populate_dict[k])
109
109
110 @classmethod
110 @classmethod
111 def query(cls):
111 def query(cls):
112 return Session().query(cls)
112 return Session().query(cls)
113
113
114 @classmethod
114 @classmethod
115 def get(cls, id_):
115 def get(cls, id_):
116 if id_:
116 if id_:
117 return cls.query().get(id_)
117 return cls.query().get(id_)
118
118
119 @classmethod
119 @classmethod
120 def get_or_404(cls, id_):
120 def get_or_404(cls, id_):
121 try:
121 try:
122 id_ = int(id_)
122 id_ = int(id_)
123 except (TypeError, ValueError):
123 except (TypeError, ValueError):
124 raise HTTPNotFound
124 raise HTTPNotFound
125
125
126 res = cls.query().get(id_)
126 res = cls.query().get(id_)
127 if not res:
127 if not res:
128 raise HTTPNotFound
128 raise HTTPNotFound
129 return res
129 return res
130
130
131 @classmethod
131 @classmethod
132 def getAll(cls):
132 def getAll(cls):
133 return cls.query().all()
133 return cls.query().all()
134
134
135 @classmethod
135 @classmethod
136 def delete(cls, id_):
136 def delete(cls, id_):
137 obj = cls.query().get(id_)
137 obj = cls.query().get(id_)
138 Session().delete(obj)
138 Session().delete(obj)
139
139
140 def __repr__(self):
140 def __repr__(self):
141 if hasattr(self, '__unicode__'):
141 if hasattr(self, '__unicode__'):
142 # python repr needs to return str
142 # python repr needs to return str
143 return safe_str(self.__unicode__())
143 return safe_str(self.__unicode__())
144 return '<DB:%s>' % (self.__class__.__name__)
144 return '<DB:%s>' % (self.__class__.__name__)
145
145
146
146
147 class RhodeCodeSetting(Base, BaseModel):
147 class RhodeCodeSetting(Base, BaseModel):
148 __tablename__ = 'rhodecode_settings'
148 __tablename__ = 'rhodecode_settings'
149 __table_args__ = (
149 __table_args__ = (
150 UniqueConstraint('app_settings_name'),
150 UniqueConstraint('app_settings_name'),
151 {'extend_existing': True, 'mysql_engine': 'InnoDB',
151 {'extend_existing': True, 'mysql_engine': 'InnoDB',
152 'mysql_charset': 'utf8'}
152 'mysql_charset': 'utf8'}
153 )
153 )
154 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
154 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
155 app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
155 app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
156 _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
156 _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
157
157
158 def __init__(self, k='', v=''):
158 def __init__(self, k='', v=''):
159 self.app_settings_name = k
159 self.app_settings_name = k
160 self.app_settings_value = v
160 self.app_settings_value = v
161
161
162 @validates('_app_settings_value')
162 @validates('_app_settings_value')
163 def validate_settings_value(self, key, val):
163 def validate_settings_value(self, key, val):
164 assert type(val) == unicode
164 assert type(val) == unicode
165 return val
165 return val
166
166
167 @hybrid_property
167 @hybrid_property
168 def app_settings_value(self):
168 def app_settings_value(self):
169 v = self._app_settings_value
169 v = self._app_settings_value
170 if self.app_settings_name == 'ldap_active':
170 if self.app_settings_name in ["ldap_active",
171 "default_repo_enable_statistics",
172 "default_repo_enable_locking",
173 "default_repo_private",
174 "default_repo_enable_downloads"]:
171 v = str2bool(v)
175 v = str2bool(v)
172 return v
176 return v
173
177
174 @app_settings_value.setter
178 @app_settings_value.setter
175 def app_settings_value(self, val):
179 def app_settings_value(self, val):
176 """
180 """
177 Setter that will always make sure we use unicode in app_settings_value
181 Setter that will always make sure we use unicode in app_settings_value
178
182
179 :param val:
183 :param val:
180 """
184 """
181 self._app_settings_value = safe_unicode(val)
185 self._app_settings_value = safe_unicode(val)
182
186
183 def __unicode__(self):
187 def __unicode__(self):
184 return u"<%s('%s:%s')>" % (
188 return u"<%s('%s:%s')>" % (
185 self.__class__.__name__,
189 self.__class__.__name__,
186 self.app_settings_name, self.app_settings_value
190 self.app_settings_name, self.app_settings_value
187 )
191 )
188
192
189 @classmethod
193 @classmethod
190 def get_by_name(cls, key):
194 def get_by_name(cls, key):
191 return cls.query()\
195 return cls.query()\
192 .filter(cls.app_settings_name == key).scalar()
196 .filter(cls.app_settings_name == key).scalar()
193
197
194 @classmethod
198 @classmethod
195 def get_by_name_or_create(cls, key):
199 def get_by_name_or_create(cls, key):
196 res = cls.get_by_name(key)
200 res = cls.get_by_name(key)
197 if not res:
201 if not res:
198 res = cls(key)
202 res = cls(key)
199 return res
203 return res
200
204
201 @classmethod
205 @classmethod
202 def get_app_settings(cls, cache=False):
206 def get_app_settings(cls, cache=False):
203
207
204 ret = cls.query()
208 ret = cls.query()
205
209
206 if cache:
210 if cache:
207 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
211 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
208
212
209 if not ret:
213 if not ret:
210 raise Exception('Could not get application settings !')
214 raise Exception('Could not get application settings !')
211 settings = {}
215 settings = {}
212 for each in ret:
216 for each in ret:
213 settings['rhodecode_' + each.app_settings_name] = \
217 settings['rhodecode_' + each.app_settings_name] = \
214 each.app_settings_value
218 each.app_settings_value
215
219
216 return settings
220 return settings
217
221
218 @classmethod
222 @classmethod
219 def get_ldap_settings(cls, cache=False):
223 def get_ldap_settings(cls, cache=False):
220 ret = cls.query()\
224 ret = cls.query()\
221 .filter(cls.app_settings_name.startswith('ldap_')).all()
225 .filter(cls.app_settings_name.startswith('ldap_')).all()
222 fd = {}
226 fd = {}
223 for row in ret:
227 for row in ret:
224 fd.update({row.app_settings_name: row.app_settings_value})
228 fd.update({row.app_settings_name: row.app_settings_value})
225
229
226 return fd
230 return fd
227
231
232 @classmethod
233 def get_default_repo_settings(cls, cache=False, strip_prefix=False):
234 ret = cls.query()\
235 .filter(cls.app_settings_name.startswith('default_')).all()
236 fd = {}
237 for row in ret:
238 key = row.app_settings_name
239 if strip_prefix:
240 key = remove_prefix(key, prefix='default_')
241 fd.update({key: row.app_settings_value})
242
243 return fd
244
228
245
229 class RhodeCodeUi(Base, BaseModel):
246 class RhodeCodeUi(Base, BaseModel):
230 __tablename__ = 'rhodecode_ui'
247 __tablename__ = 'rhodecode_ui'
231 __table_args__ = (
248 __table_args__ = (
232 UniqueConstraint('ui_key'),
249 UniqueConstraint('ui_key'),
233 {'extend_existing': True, 'mysql_engine': 'InnoDB',
250 {'extend_existing': True, 'mysql_engine': 'InnoDB',
234 'mysql_charset': 'utf8'}
251 'mysql_charset': 'utf8'}
235 )
252 )
236
253
237 HOOK_UPDATE = 'changegroup.update'
254 HOOK_UPDATE = 'changegroup.update'
238 HOOK_REPO_SIZE = 'changegroup.repo_size'
255 HOOK_REPO_SIZE = 'changegroup.repo_size'
239 HOOK_PUSH = 'changegroup.push_logger'
256 HOOK_PUSH = 'changegroup.push_logger'
240 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
257 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
241 HOOK_PULL = 'outgoing.pull_logger'
258 HOOK_PULL = 'outgoing.pull_logger'
242 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
259 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
243
260
244 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
261 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
245 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
262 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
246 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
263 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
247 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
264 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
248 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
265 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
249
266
250 @classmethod
267 @classmethod
251 def get_by_key(cls, key):
268 def get_by_key(cls, key):
252 return cls.query().filter(cls.ui_key == key).scalar()
269 return cls.query().filter(cls.ui_key == key).scalar()
253
270
254 @classmethod
271 @classmethod
255 def get_builtin_hooks(cls):
272 def get_builtin_hooks(cls):
256 q = cls.query()
273 q = cls.query()
257 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
274 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
258 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
275 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
259 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
276 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
260 return q.all()
277 return q.all()
261
278
262 @classmethod
279 @classmethod
263 def get_custom_hooks(cls):
280 def get_custom_hooks(cls):
264 q = cls.query()
281 q = cls.query()
265 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
282 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
266 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
283 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
267 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
284 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
268 q = q.filter(cls.ui_section == 'hooks')
285 q = q.filter(cls.ui_section == 'hooks')
269 return q.all()
286 return q.all()
270
287
271 @classmethod
288 @classmethod
272 def get_repos_location(cls):
289 def get_repos_location(cls):
273 return cls.get_by_key('/').ui_value
290 return cls.get_by_key('/').ui_value
274
291
275 @classmethod
292 @classmethod
276 def create_or_update_hook(cls, key, val):
293 def create_or_update_hook(cls, key, val):
277 new_ui = cls.get_by_key(key) or cls()
294 new_ui = cls.get_by_key(key) or cls()
278 new_ui.ui_section = 'hooks'
295 new_ui.ui_section = 'hooks'
279 new_ui.ui_active = True
296 new_ui.ui_active = True
280 new_ui.ui_key = key
297 new_ui.ui_key = key
281 new_ui.ui_value = val
298 new_ui.ui_value = val
282
299
283 Session().add(new_ui)
300 Session().add(new_ui)
284
301
285 def __repr__(self):
302 def __repr__(self):
286 return '<DB:%s[%s:%s]>' % (self.__class__.__name__, self.ui_key,
303 return '<DB:%s[%s:%s]>' % (self.__class__.__name__, self.ui_key,
287 self.ui_value)
304 self.ui_value)
288
305
289
306
290 class User(Base, BaseModel):
307 class User(Base, BaseModel):
291 __tablename__ = 'users'
308 __tablename__ = 'users'
292 __table_args__ = (
309 __table_args__ = (
293 UniqueConstraint('username'), UniqueConstraint('email'),
310 UniqueConstraint('username'), UniqueConstraint('email'),
294 Index('u_username_idx', 'username'),
311 Index('u_username_idx', 'username'),
295 Index('u_email_idx', 'email'),
312 Index('u_email_idx', 'email'),
296 {'extend_existing': True, 'mysql_engine': 'InnoDB',
313 {'extend_existing': True, 'mysql_engine': 'InnoDB',
297 'mysql_charset': 'utf8'}
314 'mysql_charset': 'utf8'}
298 )
315 )
299 DEFAULT_USER = 'default'
316 DEFAULT_USER = 'default'
300 DEFAULT_PERMISSIONS = [
317 DEFAULT_PERMISSIONS = [
301 'hg.register.manual_activate', 'hg.create.repository',
318 'hg.register.manual_activate', 'hg.create.repository',
302 'hg.fork.repository', 'repository.read', 'group.read'
319 'hg.fork.repository', 'repository.read', 'group.read'
303 ]
320 ]
304 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
321 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
305 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
322 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
306 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
323 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
307 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
324 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
308 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
325 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
309 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
326 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
310 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
327 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
311 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
328 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
312 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
329 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
313 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
330 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
314 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
331 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
315 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
332 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
316
333
317 user_log = relationship('UserLog', cascade='all')
334 user_log = relationship('UserLog', cascade='all')
318 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
335 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
319
336
320 repositories = relationship('Repository')
337 repositories = relationship('Repository')
321 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
338 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
322 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
339 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
323 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
340 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
324
341
325 group_member = relationship('UsersGroupMember', cascade='all')
342 group_member = relationship('UsersGroupMember', cascade='all')
326
343
327 notifications = relationship('UserNotification', cascade='all')
344 notifications = relationship('UserNotification', cascade='all')
328 # notifications assigned to this user
345 # notifications assigned to this user
329 user_created_notifications = relationship('Notification', cascade='all')
346 user_created_notifications = relationship('Notification', cascade='all')
330 # comments created by this user
347 # comments created by this user
331 user_comments = relationship('ChangesetComment', cascade='all')
348 user_comments = relationship('ChangesetComment', cascade='all')
332 #extra emails for this user
349 #extra emails for this user
333 user_emails = relationship('UserEmailMap', cascade='all')
350 user_emails = relationship('UserEmailMap', cascade='all')
334
351
335 @hybrid_property
352 @hybrid_property
336 def email(self):
353 def email(self):
337 return self._email
354 return self._email
338
355
339 @email.setter
356 @email.setter
340 def email(self, val):
357 def email(self, val):
341 self._email = val.lower() if val else None
358 self._email = val.lower() if val else None
342
359
343 @property
360 @property
344 def firstname(self):
361 def firstname(self):
345 # alias for future
362 # alias for future
346 return self.name
363 return self.name
347
364
348 @property
365 @property
349 def emails(self):
366 def emails(self):
350 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
367 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
351 return [self.email] + [x.email for x in other]
368 return [self.email] + [x.email for x in other]
352
369
353 @property
370 @property
354 def username_and_name(self):
371 def username_and_name(self):
355 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
372 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
356
373
357 @property
374 @property
358 def full_name(self):
375 def full_name(self):
359 return '%s %s' % (self.firstname, self.lastname)
376 return '%s %s' % (self.firstname, self.lastname)
360
377
361 @property
378 @property
362 def full_name_or_username(self):
379 def full_name_or_username(self):
363 return ('%s %s' % (self.firstname, self.lastname)
380 return ('%s %s' % (self.firstname, self.lastname)
364 if (self.firstname and self.lastname) else self.username)
381 if (self.firstname and self.lastname) else self.username)
365
382
366 @property
383 @property
367 def full_contact(self):
384 def full_contact(self):
368 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
385 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
369
386
370 @property
387 @property
371 def short_contact(self):
388 def short_contact(self):
372 return '%s %s' % (self.firstname, self.lastname)
389 return '%s %s' % (self.firstname, self.lastname)
373
390
374 @property
391 @property
375 def is_admin(self):
392 def is_admin(self):
376 return self.admin
393 return self.admin
377
394
378 def __unicode__(self):
395 def __unicode__(self):
379 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
396 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
380 self.user_id, self.username)
397 self.user_id, self.username)
381
398
382 @classmethod
399 @classmethod
383 def get_by_username(cls, username, case_insensitive=False, cache=False):
400 def get_by_username(cls, username, case_insensitive=False, cache=False):
384 if case_insensitive:
401 if case_insensitive:
385 q = cls.query().filter(cls.username.ilike(username))
402 q = cls.query().filter(cls.username.ilike(username))
386 else:
403 else:
387 q = cls.query().filter(cls.username == username)
404 q = cls.query().filter(cls.username == username)
388
405
389 if cache:
406 if cache:
390 q = q.options(FromCache(
407 q = q.options(FromCache(
391 "sql_cache_short",
408 "sql_cache_short",
392 "get_user_%s" % _hash_key(username)
409 "get_user_%s" % _hash_key(username)
393 )
410 )
394 )
411 )
395 return q.scalar()
412 return q.scalar()
396
413
397 @classmethod
414 @classmethod
398 def get_by_api_key(cls, api_key, cache=False):
415 def get_by_api_key(cls, api_key, cache=False):
399 q = cls.query().filter(cls.api_key == api_key)
416 q = cls.query().filter(cls.api_key == api_key)
400
417
401 if cache:
418 if cache:
402 q = q.options(FromCache("sql_cache_short",
419 q = q.options(FromCache("sql_cache_short",
403 "get_api_key_%s" % api_key))
420 "get_api_key_%s" % api_key))
404 return q.scalar()
421 return q.scalar()
405
422
406 @classmethod
423 @classmethod
407 def get_by_email(cls, email, case_insensitive=False, cache=False):
424 def get_by_email(cls, email, case_insensitive=False, cache=False):
408 if case_insensitive:
425 if case_insensitive:
409 q = cls.query().filter(cls.email.ilike(email))
426 q = cls.query().filter(cls.email.ilike(email))
410 else:
427 else:
411 q = cls.query().filter(cls.email == email)
428 q = cls.query().filter(cls.email == email)
412
429
413 if cache:
430 if cache:
414 q = q.options(FromCache("sql_cache_short",
431 q = q.options(FromCache("sql_cache_short",
415 "get_email_key_%s" % email))
432 "get_email_key_%s" % email))
416
433
417 ret = q.scalar()
434 ret = q.scalar()
418 if ret is None:
435 if ret is None:
419 q = UserEmailMap.query()
436 q = UserEmailMap.query()
420 # try fetching in alternate email map
437 # try fetching in alternate email map
421 if case_insensitive:
438 if case_insensitive:
422 q = q.filter(UserEmailMap.email.ilike(email))
439 q = q.filter(UserEmailMap.email.ilike(email))
423 else:
440 else:
424 q = q.filter(UserEmailMap.email == email)
441 q = q.filter(UserEmailMap.email == email)
425 q = q.options(joinedload(UserEmailMap.user))
442 q = q.options(joinedload(UserEmailMap.user))
426 if cache:
443 if cache:
427 q = q.options(FromCache("sql_cache_short",
444 q = q.options(FromCache("sql_cache_short",
428 "get_email_map_key_%s" % email))
445 "get_email_map_key_%s" % email))
429 ret = getattr(q.scalar(), 'user', None)
446 ret = getattr(q.scalar(), 'user', None)
430
447
431 return ret
448 return ret
432
449
433 def update_lastlogin(self):
450 def update_lastlogin(self):
434 """Update user lastlogin"""
451 """Update user lastlogin"""
435 self.last_login = datetime.datetime.now()
452 self.last_login = datetime.datetime.now()
436 Session().add(self)
453 Session().add(self)
437 log.debug('updated user %s lastlogin' % self.username)
454 log.debug('updated user %s lastlogin' % self.username)
438
455
439 def get_api_data(self):
456 def get_api_data(self):
440 """
457 """
441 Common function for generating user related data for API
458 Common function for generating user related data for API
442 """
459 """
443 user = self
460 user = self
444 data = dict(
461 data = dict(
445 user_id=user.user_id,
462 user_id=user.user_id,
446 username=user.username,
463 username=user.username,
447 firstname=user.name,
464 firstname=user.name,
448 lastname=user.lastname,
465 lastname=user.lastname,
449 email=user.email,
466 email=user.email,
450 emails=user.emails,
467 emails=user.emails,
451 api_key=user.api_key,
468 api_key=user.api_key,
452 active=user.active,
469 active=user.active,
453 admin=user.admin,
470 admin=user.admin,
454 ldap_dn=user.ldap_dn,
471 ldap_dn=user.ldap_dn,
455 last_login=user.last_login,
472 last_login=user.last_login,
456 )
473 )
457 return data
474 return data
458
475
459 def __json__(self):
476 def __json__(self):
460 data = dict(
477 data = dict(
461 full_name=self.full_name,
478 full_name=self.full_name,
462 full_name_or_username=self.full_name_or_username,
479 full_name_or_username=self.full_name_or_username,
463 short_contact=self.short_contact,
480 short_contact=self.short_contact,
464 full_contact=self.full_contact
481 full_contact=self.full_contact
465 )
482 )
466 data.update(self.get_api_data())
483 data.update(self.get_api_data())
467 return data
484 return data
468
485
469
486
470 class UserEmailMap(Base, BaseModel):
487 class UserEmailMap(Base, BaseModel):
471 __tablename__ = 'user_email_map'
488 __tablename__ = 'user_email_map'
472 __table_args__ = (
489 __table_args__ = (
473 Index('uem_email_idx', 'email'),
490 Index('uem_email_idx', 'email'),
474 UniqueConstraint('email'),
491 UniqueConstraint('email'),
475 {'extend_existing': True, 'mysql_engine': 'InnoDB',
492 {'extend_existing': True, 'mysql_engine': 'InnoDB',
476 'mysql_charset': 'utf8'}
493 'mysql_charset': 'utf8'}
477 )
494 )
478 __mapper_args__ = {}
495 __mapper_args__ = {}
479
496
480 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
497 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
481 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
498 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
482 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
499 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
483 user = relationship('User', lazy='joined')
500 user = relationship('User', lazy='joined')
484
501
485 @validates('_email')
502 @validates('_email')
486 def validate_email(self, key, email):
503 def validate_email(self, key, email):
487 # check if this email is not main one
504 # check if this email is not main one
488 main_email = Session().query(User).filter(User.email == email).scalar()
505 main_email = Session().query(User).filter(User.email == email).scalar()
489 if main_email is not None:
506 if main_email is not None:
490 raise AttributeError('email %s is present is user table' % email)
507 raise AttributeError('email %s is present is user table' % email)
491 return email
508 return email
492
509
493 @hybrid_property
510 @hybrid_property
494 def email(self):
511 def email(self):
495 return self._email
512 return self._email
496
513
497 @email.setter
514 @email.setter
498 def email(self, val):
515 def email(self, val):
499 self._email = val.lower() if val else None
516 self._email = val.lower() if val else None
500
517
501
518
502 class UserLog(Base, BaseModel):
519 class UserLog(Base, BaseModel):
503 __tablename__ = 'user_logs'
520 __tablename__ = 'user_logs'
504 __table_args__ = (
521 __table_args__ = (
505 {'extend_existing': True, 'mysql_engine': 'InnoDB',
522 {'extend_existing': True, 'mysql_engine': 'InnoDB',
506 'mysql_charset': 'utf8'},
523 'mysql_charset': 'utf8'},
507 )
524 )
508 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
525 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
509 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
526 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
510 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
527 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
511 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
528 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
512 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
529 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
513 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
530 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
514 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
531 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
515
532
516 @property
533 @property
517 def action_as_day(self):
534 def action_as_day(self):
518 return datetime.date(*self.action_date.timetuple()[:3])
535 return datetime.date(*self.action_date.timetuple()[:3])
519
536
520 user = relationship('User')
537 user = relationship('User')
521 repository = relationship('Repository', cascade='')
538 repository = relationship('Repository', cascade='')
522
539
523
540
524 class UsersGroup(Base, BaseModel):
541 class UsersGroup(Base, BaseModel):
525 __tablename__ = 'users_groups'
542 __tablename__ = 'users_groups'
526 __table_args__ = (
543 __table_args__ = (
527 {'extend_existing': True, 'mysql_engine': 'InnoDB',
544 {'extend_existing': True, 'mysql_engine': 'InnoDB',
528 'mysql_charset': 'utf8'},
545 'mysql_charset': 'utf8'},
529 )
546 )
530
547
531 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
548 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
532 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
549 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
533 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
550 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
534 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
551 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
535
552
536 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
553 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
537 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
554 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
538 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
555 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
539
556
540 def __unicode__(self):
557 def __unicode__(self):
541 return u'<userGroup(%s)>' % (self.users_group_name)
558 return u'<userGroup(%s)>' % (self.users_group_name)
542
559
543 @classmethod
560 @classmethod
544 def get_by_group_name(cls, group_name, cache=False,
561 def get_by_group_name(cls, group_name, cache=False,
545 case_insensitive=False):
562 case_insensitive=False):
546 if case_insensitive:
563 if case_insensitive:
547 q = cls.query().filter(cls.users_group_name.ilike(group_name))
564 q = cls.query().filter(cls.users_group_name.ilike(group_name))
548 else:
565 else:
549 q = cls.query().filter(cls.users_group_name == group_name)
566 q = cls.query().filter(cls.users_group_name == group_name)
550 if cache:
567 if cache:
551 q = q.options(FromCache(
568 q = q.options(FromCache(
552 "sql_cache_short",
569 "sql_cache_short",
553 "get_user_%s" % _hash_key(group_name)
570 "get_user_%s" % _hash_key(group_name)
554 )
571 )
555 )
572 )
556 return q.scalar()
573 return q.scalar()
557
574
558 @classmethod
575 @classmethod
559 def get(cls, users_group_id, cache=False):
576 def get(cls, users_group_id, cache=False):
560 users_group = cls.query()
577 users_group = cls.query()
561 if cache:
578 if cache:
562 users_group = users_group.options(FromCache("sql_cache_short",
579 users_group = users_group.options(FromCache("sql_cache_short",
563 "get_users_group_%s" % users_group_id))
580 "get_users_group_%s" % users_group_id))
564 return users_group.get(users_group_id)
581 return users_group.get(users_group_id)
565
582
566 def get_api_data(self):
583 def get_api_data(self):
567 users_group = self
584 users_group = self
568
585
569 data = dict(
586 data = dict(
570 users_group_id=users_group.users_group_id,
587 users_group_id=users_group.users_group_id,
571 group_name=users_group.users_group_name,
588 group_name=users_group.users_group_name,
572 active=users_group.users_group_active,
589 active=users_group.users_group_active,
573 )
590 )
574
591
575 return data
592 return data
576
593
577
594
578 class UsersGroupMember(Base, BaseModel):
595 class UsersGroupMember(Base, BaseModel):
579 __tablename__ = 'users_groups_members'
596 __tablename__ = 'users_groups_members'
580 __table_args__ = (
597 __table_args__ = (
581 {'extend_existing': True, 'mysql_engine': 'InnoDB',
598 {'extend_existing': True, 'mysql_engine': 'InnoDB',
582 'mysql_charset': 'utf8'},
599 'mysql_charset': 'utf8'},
583 )
600 )
584
601
585 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
602 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
586 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
603 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
587 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
604 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
588
605
589 user = relationship('User', lazy='joined')
606 user = relationship('User', lazy='joined')
590 users_group = relationship('UsersGroup')
607 users_group = relationship('UsersGroup')
591
608
592 def __init__(self, gr_id='', u_id=''):
609 def __init__(self, gr_id='', u_id=''):
593 self.users_group_id = gr_id
610 self.users_group_id = gr_id
594 self.user_id = u_id
611 self.user_id = u_id
595
612
596
613
597 class Repository(Base, BaseModel):
614 class Repository(Base, BaseModel):
598 __tablename__ = 'repositories'
615 __tablename__ = 'repositories'
599 __table_args__ = (
616 __table_args__ = (
600 UniqueConstraint('repo_name'),
617 UniqueConstraint('repo_name'),
601 Index('r_repo_name_idx', 'repo_name'),
618 Index('r_repo_name_idx', 'repo_name'),
602 {'extend_existing': True, 'mysql_engine': 'InnoDB',
619 {'extend_existing': True, 'mysql_engine': 'InnoDB',
603 'mysql_charset': 'utf8'},
620 'mysql_charset': 'utf8'},
604 )
621 )
605
622
606 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
623 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
607 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
624 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
608 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
625 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
609 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
626 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
610 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
627 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
611 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
628 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
612 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
629 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
613 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
630 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
614 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
631 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
615 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
632 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
616 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
633 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
617 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
634 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
618 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
635 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
619 _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
636 _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
620
637
621 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
638 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
622 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
639 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
623
640
624 user = relationship('User')
641 user = relationship('User')
625 fork = relationship('Repository', remote_side=repo_id)
642 fork = relationship('Repository', remote_side=repo_id)
626 group = relationship('RepoGroup')
643 group = relationship('RepoGroup')
627 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
644 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
628 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
645 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
629 stats = relationship('Statistics', cascade='all', uselist=False)
646 stats = relationship('Statistics', cascade='all', uselist=False)
630
647
631 followers = relationship('UserFollowing',
648 followers = relationship('UserFollowing',
632 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
649 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
633 cascade='all')
650 cascade='all')
634
651
635 logs = relationship('UserLog')
652 logs = relationship('UserLog')
636 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
653 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
637
654
638 pull_requests_org = relationship('PullRequest',
655 pull_requests_org = relationship('PullRequest',
639 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
656 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
640 cascade="all, delete, delete-orphan")
657 cascade="all, delete, delete-orphan")
641
658
642 pull_requests_other = relationship('PullRequest',
659 pull_requests_other = relationship('PullRequest',
643 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
660 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
644 cascade="all, delete, delete-orphan")
661 cascade="all, delete, delete-orphan")
645
662
646 def __unicode__(self):
663 def __unicode__(self):
647 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
664 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
648 self.repo_name)
665 self.repo_name)
649
666
650 @hybrid_property
667 @hybrid_property
651 def locked(self):
668 def locked(self):
652 # always should return [user_id, timelocked]
669 # always should return [user_id, timelocked]
653 if self._locked:
670 if self._locked:
654 _lock_info = self._locked.split(':')
671 _lock_info = self._locked.split(':')
655 return int(_lock_info[0]), _lock_info[1]
672 return int(_lock_info[0]), _lock_info[1]
656 return [None, None]
673 return [None, None]
657
674
658 @locked.setter
675 @locked.setter
659 def locked(self, val):
676 def locked(self, val):
660 if val and isinstance(val, (list, tuple)):
677 if val and isinstance(val, (list, tuple)):
661 self._locked = ':'.join(map(str, val))
678 self._locked = ':'.join(map(str, val))
662 else:
679 else:
663 self._locked = None
680 self._locked = None
664
681
665 @classmethod
682 @classmethod
666 def url_sep(cls):
683 def url_sep(cls):
667 return URL_SEP
684 return URL_SEP
668
685
669 @classmethod
686 @classmethod
670 def get_by_repo_name(cls, repo_name):
687 def get_by_repo_name(cls, repo_name):
671 q = Session().query(cls).filter(cls.repo_name == repo_name)
688 q = Session().query(cls).filter(cls.repo_name == repo_name)
672 q = q.options(joinedload(Repository.fork))\
689 q = q.options(joinedload(Repository.fork))\
673 .options(joinedload(Repository.user))\
690 .options(joinedload(Repository.user))\
674 .options(joinedload(Repository.group))
691 .options(joinedload(Repository.group))
675 return q.scalar()
692 return q.scalar()
676
693
677 @classmethod
694 @classmethod
678 def get_by_full_path(cls, repo_full_path):
695 def get_by_full_path(cls, repo_full_path):
679 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
696 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
680 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
697 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
681
698
682 @classmethod
699 @classmethod
683 def get_repo_forks(cls, repo_id):
700 def get_repo_forks(cls, repo_id):
684 return cls.query().filter(Repository.fork_id == repo_id)
701 return cls.query().filter(Repository.fork_id == repo_id)
685
702
686 @classmethod
703 @classmethod
687 def base_path(cls):
704 def base_path(cls):
688 """
705 """
689 Returns base path when all repos are stored
706 Returns base path when all repos are stored
690
707
691 :param cls:
708 :param cls:
692 """
709 """
693 q = Session().query(RhodeCodeUi)\
710 q = Session().query(RhodeCodeUi)\
694 .filter(RhodeCodeUi.ui_key == cls.url_sep())
711 .filter(RhodeCodeUi.ui_key == cls.url_sep())
695 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
712 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
696 return q.one().ui_value
713 return q.one().ui_value
697
714
698 @property
715 @property
699 def forks(self):
716 def forks(self):
700 """
717 """
701 Return forks of this repo
718 Return forks of this repo
702 """
719 """
703 return Repository.get_repo_forks(self.repo_id)
720 return Repository.get_repo_forks(self.repo_id)
704
721
705 @property
722 @property
706 def parent(self):
723 def parent(self):
707 """
724 """
708 Returns fork parent
725 Returns fork parent
709 """
726 """
710 return self.fork
727 return self.fork
711
728
712 @property
729 @property
713 def just_name(self):
730 def just_name(self):
714 return self.repo_name.split(Repository.url_sep())[-1]
731 return self.repo_name.split(Repository.url_sep())[-1]
715
732
716 @property
733 @property
717 def groups_with_parents(self):
734 def groups_with_parents(self):
718 groups = []
735 groups = []
719 if self.group is None:
736 if self.group is None:
720 return groups
737 return groups
721
738
722 cur_gr = self.group
739 cur_gr = self.group
723 groups.insert(0, cur_gr)
740 groups.insert(0, cur_gr)
724 while 1:
741 while 1:
725 gr = getattr(cur_gr, 'parent_group', None)
742 gr = getattr(cur_gr, 'parent_group', None)
726 cur_gr = cur_gr.parent_group
743 cur_gr = cur_gr.parent_group
727 if gr is None:
744 if gr is None:
728 break
745 break
729 groups.insert(0, gr)
746 groups.insert(0, gr)
730
747
731 return groups
748 return groups
732
749
733 @property
750 @property
734 def groups_and_repo(self):
751 def groups_and_repo(self):
735 return self.groups_with_parents, self.just_name
752 return self.groups_with_parents, self.just_name
736
753
737 @LazyProperty
754 @LazyProperty
738 def repo_path(self):
755 def repo_path(self):
739 """
756 """
740 Returns base full path for that repository means where it actually
757 Returns base full path for that repository means where it actually
741 exists on a filesystem
758 exists on a filesystem
742 """
759 """
743 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
760 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
744 Repository.url_sep())
761 Repository.url_sep())
745 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
762 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
746 return q.one().ui_value
763 return q.one().ui_value
747
764
748 @property
765 @property
749 def repo_full_path(self):
766 def repo_full_path(self):
750 p = [self.repo_path]
767 p = [self.repo_path]
751 # we need to split the name by / since this is how we store the
768 # we need to split the name by / since this is how we store the
752 # names in the database, but that eventually needs to be converted
769 # names in the database, but that eventually needs to be converted
753 # into a valid system path
770 # into a valid system path
754 p += self.repo_name.split(Repository.url_sep())
771 p += self.repo_name.split(Repository.url_sep())
755 return os.path.join(*p)
772 return os.path.join(*p)
756
773
757 @property
774 @property
758 def cache_keys(self):
775 def cache_keys(self):
759 """
776 """
760 Returns associated cache keys for that repo
777 Returns associated cache keys for that repo
761 """
778 """
762 return CacheInvalidation.query()\
779 return CacheInvalidation.query()\
763 .filter(CacheInvalidation.cache_args == self.repo_name)\
780 .filter(CacheInvalidation.cache_args == self.repo_name)\
764 .order_by(CacheInvalidation.cache_key)\
781 .order_by(CacheInvalidation.cache_key)\
765 .all()
782 .all()
766
783
767 def get_new_name(self, repo_name):
784 def get_new_name(self, repo_name):
768 """
785 """
769 returns new full repository name based on assigned group and new new
786 returns new full repository name based on assigned group and new new
770
787
771 :param group_name:
788 :param group_name:
772 """
789 """
773 path_prefix = self.group.full_path_splitted if self.group else []
790 path_prefix = self.group.full_path_splitted if self.group else []
774 return Repository.url_sep().join(path_prefix + [repo_name])
791 return Repository.url_sep().join(path_prefix + [repo_name])
775
792
776 @property
793 @property
777 def _ui(self):
794 def _ui(self):
778 """
795 """
779 Creates an db based ui object for this repository
796 Creates an db based ui object for this repository
780 """
797 """
781 from rhodecode.lib.utils import make_ui
798 from rhodecode.lib.utils import make_ui
782 return make_ui('db', clear_session=False)
799 return make_ui('db', clear_session=False)
783
800
784 @classmethod
801 @classmethod
785 def inject_ui(cls, repo, extras={}):
802 def inject_ui(cls, repo, extras={}):
786 from rhodecode.lib.vcs.backends.hg import MercurialRepository
803 from rhodecode.lib.vcs.backends.hg import MercurialRepository
787 from rhodecode.lib.vcs.backends.git import GitRepository
804 from rhodecode.lib.vcs.backends.git import GitRepository
788 required = (MercurialRepository, GitRepository)
805 required = (MercurialRepository, GitRepository)
789 if not isinstance(repo, required):
806 if not isinstance(repo, required):
790 raise Exception('repo must be instance of %s' % required)
807 raise Exception('repo must be instance of %s' % required)
791
808
792 # inject ui extra param to log this action via push logger
809 # inject ui extra param to log this action via push logger
793 for k, v in extras.items():
810 for k, v in extras.items():
794 repo._repo.ui.setconfig('rhodecode_extras', k, v)
811 repo._repo.ui.setconfig('rhodecode_extras', k, v)
795
812
796 @classmethod
813 @classmethod
797 def is_valid(cls, repo_name):
814 def is_valid(cls, repo_name):
798 """
815 """
799 returns True if given repo name is a valid filesystem repository
816 returns True if given repo name is a valid filesystem repository
800
817
801 :param cls:
818 :param cls:
802 :param repo_name:
819 :param repo_name:
803 """
820 """
804 from rhodecode.lib.utils import is_valid_repo
821 from rhodecode.lib.utils import is_valid_repo
805
822
806 return is_valid_repo(repo_name, cls.base_path())
823 return is_valid_repo(repo_name, cls.base_path())
807
824
808 def get_api_data(self):
825 def get_api_data(self):
809 """
826 """
810 Common function for generating repo api data
827 Common function for generating repo api data
811
828
812 """
829 """
813 repo = self
830 repo = self
814 data = dict(
831 data = dict(
815 repo_id=repo.repo_id,
832 repo_id=repo.repo_id,
816 repo_name=repo.repo_name,
833 repo_name=repo.repo_name,
817 repo_type=repo.repo_type,
834 repo_type=repo.repo_type,
818 clone_uri=repo.clone_uri,
835 clone_uri=repo.clone_uri,
819 private=repo.private,
836 private=repo.private,
820 created_on=repo.created_on,
837 created_on=repo.created_on,
821 description=repo.description,
838 description=repo.description,
822 landing_rev=repo.landing_rev,
839 landing_rev=repo.landing_rev,
823 owner=repo.user.username,
840 owner=repo.user.username,
824 fork_of=repo.fork.repo_name if repo.fork else None
841 fork_of=repo.fork.repo_name if repo.fork else None
825 )
842 )
826
843
827 return data
844 return data
828
845
829 @classmethod
846 @classmethod
830 def lock(cls, repo, user_id):
847 def lock(cls, repo, user_id):
831 repo.locked = [user_id, time.time()]
848 repo.locked = [user_id, time.time()]
832 Session().add(repo)
849 Session().add(repo)
833 Session().commit()
850 Session().commit()
834
851
835 @classmethod
852 @classmethod
836 def unlock(cls, repo):
853 def unlock(cls, repo):
837 repo.locked = None
854 repo.locked = None
838 Session().add(repo)
855 Session().add(repo)
839 Session().commit()
856 Session().commit()
840
857
841 @property
858 @property
842 def last_db_change(self):
859 def last_db_change(self):
843 return self.updated_on
860 return self.updated_on
844
861
845 #==========================================================================
862 #==========================================================================
846 # SCM PROPERTIES
863 # SCM PROPERTIES
847 #==========================================================================
864 #==========================================================================
848
865
849 def get_changeset(self, rev=None):
866 def get_changeset(self, rev=None):
850 return get_changeset_safe(self.scm_instance, rev)
867 return get_changeset_safe(self.scm_instance, rev)
851
868
852 def get_landing_changeset(self):
869 def get_landing_changeset(self):
853 """
870 """
854 Returns landing changeset, or if that doesn't exist returns the tip
871 Returns landing changeset, or if that doesn't exist returns the tip
855 """
872 """
856 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
873 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
857 return cs
874 return cs
858
875
859 def update_last_change(self, last_change=None):
876 def update_last_change(self, last_change=None):
860 if last_change is None:
877 if last_change is None:
861 last_change = datetime.datetime.now()
878 last_change = datetime.datetime.now()
862 if self.updated_on is None or self.updated_on != last_change:
879 if self.updated_on is None or self.updated_on != last_change:
863 log.debug('updated repo %s with new date %s' % (self, last_change))
880 log.debug('updated repo %s with new date %s' % (self, last_change))
864 self.updated_on = last_change
881 self.updated_on = last_change
865 Session().add(self)
882 Session().add(self)
866 Session().commit()
883 Session().commit()
867
884
868 @property
885 @property
869 def tip(self):
886 def tip(self):
870 return self.get_changeset('tip')
887 return self.get_changeset('tip')
871
888
872 @property
889 @property
873 def author(self):
890 def author(self):
874 return self.tip.author
891 return self.tip.author
875
892
876 @property
893 @property
877 def last_change(self):
894 def last_change(self):
878 return self.scm_instance.last_change
895 return self.scm_instance.last_change
879
896
880 def get_comments(self, revisions=None):
897 def get_comments(self, revisions=None):
881 """
898 """
882 Returns comments for this repository grouped by revisions
899 Returns comments for this repository grouped by revisions
883
900
884 :param revisions: filter query by revisions only
901 :param revisions: filter query by revisions only
885 """
902 """
886 cmts = ChangesetComment.query()\
903 cmts = ChangesetComment.query()\
887 .filter(ChangesetComment.repo == self)
904 .filter(ChangesetComment.repo == self)
888 if revisions:
905 if revisions:
889 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
906 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
890 grouped = defaultdict(list)
907 grouped = defaultdict(list)
891 for cmt in cmts.all():
908 for cmt in cmts.all():
892 grouped[cmt.revision].append(cmt)
909 grouped[cmt.revision].append(cmt)
893 return grouped
910 return grouped
894
911
895 def statuses(self, revisions=None):
912 def statuses(self, revisions=None):
896 """
913 """
897 Returns statuses for this repository
914 Returns statuses for this repository
898
915
899 :param revisions: list of revisions to get statuses for
916 :param revisions: list of revisions to get statuses for
900 :type revisions: list
917 :type revisions: list
901 """
918 """
902
919
903 statuses = ChangesetStatus.query()\
920 statuses = ChangesetStatus.query()\
904 .filter(ChangesetStatus.repo == self)\
921 .filter(ChangesetStatus.repo == self)\
905 .filter(ChangesetStatus.version == 0)
922 .filter(ChangesetStatus.version == 0)
906 if revisions:
923 if revisions:
907 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
924 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
908 grouped = {}
925 grouped = {}
909
926
910 #maybe we have open new pullrequest without a status ?
927 #maybe we have open new pullrequest without a status ?
911 stat = ChangesetStatus.STATUS_UNDER_REVIEW
928 stat = ChangesetStatus.STATUS_UNDER_REVIEW
912 status_lbl = ChangesetStatus.get_status_lbl(stat)
929 status_lbl = ChangesetStatus.get_status_lbl(stat)
913 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
930 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
914 for rev in pr.revisions:
931 for rev in pr.revisions:
915 pr_id = pr.pull_request_id
932 pr_id = pr.pull_request_id
916 pr_repo = pr.other_repo.repo_name
933 pr_repo = pr.other_repo.repo_name
917 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
934 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
918
935
919 for stat in statuses.all():
936 for stat in statuses.all():
920 pr_id = pr_repo = None
937 pr_id = pr_repo = None
921 if stat.pull_request:
938 if stat.pull_request:
922 pr_id = stat.pull_request.pull_request_id
939 pr_id = stat.pull_request.pull_request_id
923 pr_repo = stat.pull_request.other_repo.repo_name
940 pr_repo = stat.pull_request.other_repo.repo_name
924 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
941 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
925 pr_id, pr_repo]
942 pr_id, pr_repo]
926 return grouped
943 return grouped
927
944
928 #==========================================================================
945 #==========================================================================
929 # SCM CACHE INSTANCE
946 # SCM CACHE INSTANCE
930 #==========================================================================
947 #==========================================================================
931
948
932 @property
949 @property
933 def invalidate(self):
950 def invalidate(self):
934 return CacheInvalidation.invalidate(self.repo_name)
951 return CacheInvalidation.invalidate(self.repo_name)
935
952
936 def set_invalidate(self):
953 def set_invalidate(self):
937 """
954 """
938 set a cache for invalidation for this instance
955 set a cache for invalidation for this instance
939 """
956 """
940 CacheInvalidation.set_invalidate(repo_name=self.repo_name)
957 CacheInvalidation.set_invalidate(repo_name=self.repo_name)
941
958
942 @LazyProperty
959 @LazyProperty
943 def scm_instance(self):
960 def scm_instance(self):
944 import rhodecode
961 import rhodecode
945 full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
962 full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
946 if full_cache:
963 if full_cache:
947 return self.scm_instance_cached()
964 return self.scm_instance_cached()
948 return self.__get_instance()
965 return self.__get_instance()
949
966
950 def scm_instance_cached(self, cache_map=None):
967 def scm_instance_cached(self, cache_map=None):
951 @cache_region('long_term')
968 @cache_region('long_term')
952 def _c(repo_name):
969 def _c(repo_name):
953 return self.__get_instance()
970 return self.__get_instance()
954 rn = self.repo_name
971 rn = self.repo_name
955 log.debug('Getting cached instance of repo')
972 log.debug('Getting cached instance of repo')
956
973
957 if cache_map:
974 if cache_map:
958 # get using prefilled cache_map
975 # get using prefilled cache_map
959 invalidate_repo = cache_map[self.repo_name]
976 invalidate_repo = cache_map[self.repo_name]
960 if invalidate_repo:
977 if invalidate_repo:
961 invalidate_repo = (None if invalidate_repo.cache_active
978 invalidate_repo = (None if invalidate_repo.cache_active
962 else invalidate_repo)
979 else invalidate_repo)
963 else:
980 else:
964 # get from invalidate
981 # get from invalidate
965 invalidate_repo = self.invalidate
982 invalidate_repo = self.invalidate
966
983
967 if invalidate_repo is not None:
984 if invalidate_repo is not None:
968 region_invalidate(_c, None, rn)
985 region_invalidate(_c, None, rn)
969 # update our cache
986 # update our cache
970 CacheInvalidation.set_valid(invalidate_repo.cache_key)
987 CacheInvalidation.set_valid(invalidate_repo.cache_key)
971 return _c(rn)
988 return _c(rn)
972
989
973 def __get_instance(self):
990 def __get_instance(self):
974 repo_full_path = self.repo_full_path
991 repo_full_path = self.repo_full_path
975 try:
992 try:
976 alias = get_scm(repo_full_path)[0]
993 alias = get_scm(repo_full_path)[0]
977 log.debug('Creating instance of %s repository' % alias)
994 log.debug('Creating instance of %s repository' % alias)
978 backend = get_backend(alias)
995 backend = get_backend(alias)
979 except VCSError:
996 except VCSError:
980 log.error(traceback.format_exc())
997 log.error(traceback.format_exc())
981 log.error('Perhaps this repository is in db and not in '
998 log.error('Perhaps this repository is in db and not in '
982 'filesystem run rescan repositories with '
999 'filesystem run rescan repositories with '
983 '"destroy old data " option from admin panel')
1000 '"destroy old data " option from admin panel')
984 return
1001 return
985
1002
986 if alias == 'hg':
1003 if alias == 'hg':
987
1004
988 repo = backend(safe_str(repo_full_path), create=False,
1005 repo = backend(safe_str(repo_full_path), create=False,
989 baseui=self._ui)
1006 baseui=self._ui)
990 # skip hidden web repository
1007 # skip hidden web repository
991 if repo._get_hidden():
1008 if repo._get_hidden():
992 return
1009 return
993 else:
1010 else:
994 repo = backend(repo_full_path, create=False)
1011 repo = backend(repo_full_path, create=False)
995
1012
996 return repo
1013 return repo
997
1014
998
1015
999 class RepoGroup(Base, BaseModel):
1016 class RepoGroup(Base, BaseModel):
1000 __tablename__ = 'groups'
1017 __tablename__ = 'groups'
1001 __table_args__ = (
1018 __table_args__ = (
1002 UniqueConstraint('group_name', 'group_parent_id'),
1019 UniqueConstraint('group_name', 'group_parent_id'),
1003 CheckConstraint('group_id != group_parent_id'),
1020 CheckConstraint('group_id != group_parent_id'),
1004 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1021 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1005 'mysql_charset': 'utf8'},
1022 'mysql_charset': 'utf8'},
1006 )
1023 )
1007 __mapper_args__ = {'order_by': 'group_name'}
1024 __mapper_args__ = {'order_by': 'group_name'}
1008
1025
1009 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1026 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1010 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
1027 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
1011 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
1028 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
1012 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1029 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1013 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
1030 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
1014
1031
1015 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
1032 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
1016 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
1033 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
1017
1034
1018 parent_group = relationship('RepoGroup', remote_side=group_id)
1035 parent_group = relationship('RepoGroup', remote_side=group_id)
1019
1036
1020 def __init__(self, group_name='', parent_group=None):
1037 def __init__(self, group_name='', parent_group=None):
1021 self.group_name = group_name
1038 self.group_name = group_name
1022 self.parent_group = parent_group
1039 self.parent_group = parent_group
1023
1040
1024 def __unicode__(self):
1041 def __unicode__(self):
1025 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
1042 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
1026 self.group_name)
1043 self.group_name)
1027
1044
1028 @classmethod
1045 @classmethod
1029 def groups_choices(cls, check_perms=False):
1046 def groups_choices(cls, check_perms=False):
1030 from webhelpers.html import literal as _literal
1047 from webhelpers.html import literal as _literal
1031 from rhodecode.model.scm import ScmModel
1048 from rhodecode.model.scm import ScmModel
1032 groups = cls.query().all()
1049 groups = cls.query().all()
1033 if check_perms:
1050 if check_perms:
1034 #filter group user have access to, it's done
1051 #filter group user have access to, it's done
1035 #magically inside ScmModel based on current user
1052 #magically inside ScmModel based on current user
1036 groups = ScmModel().get_repos_groups(groups)
1053 groups = ScmModel().get_repos_groups(groups)
1037 repo_groups = [('', '')]
1054 repo_groups = [('', '')]
1038 sep = ' &raquo; '
1055 sep = ' &raquo; '
1039 _name = lambda k: _literal(sep.join(k))
1056 _name = lambda k: _literal(sep.join(k))
1040
1057
1041 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
1058 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
1042 for x in groups])
1059 for x in groups])
1043
1060
1044 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
1061 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
1045 return repo_groups
1062 return repo_groups
1046
1063
1047 @classmethod
1064 @classmethod
1048 def url_sep(cls):
1065 def url_sep(cls):
1049 return URL_SEP
1066 return URL_SEP
1050
1067
1051 @classmethod
1068 @classmethod
1052 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
1069 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
1053 if case_insensitive:
1070 if case_insensitive:
1054 gr = cls.query()\
1071 gr = cls.query()\
1055 .filter(cls.group_name.ilike(group_name))
1072 .filter(cls.group_name.ilike(group_name))
1056 else:
1073 else:
1057 gr = cls.query()\
1074 gr = cls.query()\
1058 .filter(cls.group_name == group_name)
1075 .filter(cls.group_name == group_name)
1059 if cache:
1076 if cache:
1060 gr = gr.options(FromCache(
1077 gr = gr.options(FromCache(
1061 "sql_cache_short",
1078 "sql_cache_short",
1062 "get_group_%s" % _hash_key(group_name)
1079 "get_group_%s" % _hash_key(group_name)
1063 )
1080 )
1064 )
1081 )
1065 return gr.scalar()
1082 return gr.scalar()
1066
1083
1067 @property
1084 @property
1068 def parents(self):
1085 def parents(self):
1069 parents_recursion_limit = 5
1086 parents_recursion_limit = 5
1070 groups = []
1087 groups = []
1071 if self.parent_group is None:
1088 if self.parent_group is None:
1072 return groups
1089 return groups
1073 cur_gr = self.parent_group
1090 cur_gr = self.parent_group
1074 groups.insert(0, cur_gr)
1091 groups.insert(0, cur_gr)
1075 cnt = 0
1092 cnt = 0
1076 while 1:
1093 while 1:
1077 cnt += 1
1094 cnt += 1
1078 gr = getattr(cur_gr, 'parent_group', None)
1095 gr = getattr(cur_gr, 'parent_group', None)
1079 cur_gr = cur_gr.parent_group
1096 cur_gr = cur_gr.parent_group
1080 if gr is None:
1097 if gr is None:
1081 break
1098 break
1082 if cnt == parents_recursion_limit:
1099 if cnt == parents_recursion_limit:
1083 # this will prevent accidental infinit loops
1100 # this will prevent accidental infinit loops
1084 log.error('group nested more than %s' %
1101 log.error('group nested more than %s' %
1085 parents_recursion_limit)
1102 parents_recursion_limit)
1086 break
1103 break
1087
1104
1088 groups.insert(0, gr)
1105 groups.insert(0, gr)
1089 return groups
1106 return groups
1090
1107
1091 @property
1108 @property
1092 def children(self):
1109 def children(self):
1093 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1110 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1094
1111
1095 @property
1112 @property
1096 def name(self):
1113 def name(self):
1097 return self.group_name.split(RepoGroup.url_sep())[-1]
1114 return self.group_name.split(RepoGroup.url_sep())[-1]
1098
1115
1099 @property
1116 @property
1100 def full_path(self):
1117 def full_path(self):
1101 return self.group_name
1118 return self.group_name
1102
1119
1103 @property
1120 @property
1104 def full_path_splitted(self):
1121 def full_path_splitted(self):
1105 return self.group_name.split(RepoGroup.url_sep())
1122 return self.group_name.split(RepoGroup.url_sep())
1106
1123
1107 @property
1124 @property
1108 def repositories(self):
1125 def repositories(self):
1109 return Repository.query()\
1126 return Repository.query()\
1110 .filter(Repository.group == self)\
1127 .filter(Repository.group == self)\
1111 .order_by(Repository.repo_name)
1128 .order_by(Repository.repo_name)
1112
1129
1113 @property
1130 @property
1114 def repositories_recursive_count(self):
1131 def repositories_recursive_count(self):
1115 cnt = self.repositories.count()
1132 cnt = self.repositories.count()
1116
1133
1117 def children_count(group):
1134 def children_count(group):
1118 cnt = 0
1135 cnt = 0
1119 for child in group.children:
1136 for child in group.children:
1120 cnt += child.repositories.count()
1137 cnt += child.repositories.count()
1121 cnt += children_count(child)
1138 cnt += children_count(child)
1122 return cnt
1139 return cnt
1123
1140
1124 return cnt + children_count(self)
1141 return cnt + children_count(self)
1125
1142
1126 def recursive_groups_and_repos(self):
1143 def recursive_groups_and_repos(self):
1127 """
1144 """
1128 Recursive return all groups, with repositories in those groups
1145 Recursive return all groups, with repositories in those groups
1129 """
1146 """
1130 all_ = []
1147 all_ = []
1131
1148
1132 def _get_members(root_gr):
1149 def _get_members(root_gr):
1133 for r in root_gr.repositories:
1150 for r in root_gr.repositories:
1134 all_.append(r)
1151 all_.append(r)
1135 childs = root_gr.children.all()
1152 childs = root_gr.children.all()
1136 if childs:
1153 if childs:
1137 for gr in childs:
1154 for gr in childs:
1138 all_.append(gr)
1155 all_.append(gr)
1139 _get_members(gr)
1156 _get_members(gr)
1140
1157
1141 _get_members(self)
1158 _get_members(self)
1142 return [self] + all_
1159 return [self] + all_
1143
1160
1144 def get_new_name(self, group_name):
1161 def get_new_name(self, group_name):
1145 """
1162 """
1146 returns new full group name based on parent and new name
1163 returns new full group name based on parent and new name
1147
1164
1148 :param group_name:
1165 :param group_name:
1149 """
1166 """
1150 path_prefix = (self.parent_group.full_path_splitted if
1167 path_prefix = (self.parent_group.full_path_splitted if
1151 self.parent_group else [])
1168 self.parent_group else [])
1152 return RepoGroup.url_sep().join(path_prefix + [group_name])
1169 return RepoGroup.url_sep().join(path_prefix + [group_name])
1153
1170
1154
1171
1155 class Permission(Base, BaseModel):
1172 class Permission(Base, BaseModel):
1156 __tablename__ = 'permissions'
1173 __tablename__ = 'permissions'
1157 __table_args__ = (
1174 __table_args__ = (
1158 Index('p_perm_name_idx', 'permission_name'),
1175 Index('p_perm_name_idx', 'permission_name'),
1159 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1176 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1160 'mysql_charset': 'utf8'},
1177 'mysql_charset': 'utf8'},
1161 )
1178 )
1162 PERMS = [
1179 PERMS = [
1163 ('repository.none', _('Repository no access')),
1180 ('repository.none', _('Repository no access')),
1164 ('repository.read', _('Repository read access')),
1181 ('repository.read', _('Repository read access')),
1165 ('repository.write', _('Repository write access')),
1182 ('repository.write', _('Repository write access')),
1166 ('repository.admin', _('Repository admin access')),
1183 ('repository.admin', _('Repository admin access')),
1167
1184
1168 ('group.none', _('Repositories Group no access')),
1185 ('group.none', _('Repositories Group no access')),
1169 ('group.read', _('Repositories Group read access')),
1186 ('group.read', _('Repositories Group read access')),
1170 ('group.write', _('Repositories Group write access')),
1187 ('group.write', _('Repositories Group write access')),
1171 ('group.admin', _('Repositories Group admin access')),
1188 ('group.admin', _('Repositories Group admin access')),
1172
1189
1173 ('hg.admin', _('RhodeCode Administrator')),
1190 ('hg.admin', _('RhodeCode Administrator')),
1174 ('hg.create.none', _('Repository creation disabled')),
1191 ('hg.create.none', _('Repository creation disabled')),
1175 ('hg.create.repository', _('Repository creation enabled')),
1192 ('hg.create.repository', _('Repository creation enabled')),
1176 ('hg.fork.none', _('Repository forking disabled')),
1193 ('hg.fork.none', _('Repository forking disabled')),
1177 ('hg.fork.repository', _('Repository forking enabled')),
1194 ('hg.fork.repository', _('Repository forking enabled')),
1178 ('hg.register.none', _('Register disabled')),
1195 ('hg.register.none', _('Register disabled')),
1179 ('hg.register.manual_activate', _('Register new user with RhodeCode '
1196 ('hg.register.manual_activate', _('Register new user with RhodeCode '
1180 'with manual activation')),
1197 'with manual activation')),
1181
1198
1182 ('hg.register.auto_activate', _('Register new user with RhodeCode '
1199 ('hg.register.auto_activate', _('Register new user with RhodeCode '
1183 'with auto activation')),
1200 'with auto activation')),
1184 ]
1201 ]
1185
1202
1186 # defines which permissions are more important higher the more important
1203 # defines which permissions are more important higher the more important
1187 PERM_WEIGHTS = {
1204 PERM_WEIGHTS = {
1188 'repository.none': 0,
1205 'repository.none': 0,
1189 'repository.read': 1,
1206 'repository.read': 1,
1190 'repository.write': 3,
1207 'repository.write': 3,
1191 'repository.admin': 4,
1208 'repository.admin': 4,
1192
1209
1193 'group.none': 0,
1210 'group.none': 0,
1194 'group.read': 1,
1211 'group.read': 1,
1195 'group.write': 3,
1212 'group.write': 3,
1196 'group.admin': 4,
1213 'group.admin': 4,
1197
1214
1198 'hg.fork.none': 0,
1215 'hg.fork.none': 0,
1199 'hg.fork.repository': 1,
1216 'hg.fork.repository': 1,
1200 'hg.create.none': 0,
1217 'hg.create.none': 0,
1201 'hg.create.repository':1
1218 'hg.create.repository':1
1202 }
1219 }
1203
1220
1204 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1221 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1205 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1222 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1206 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1223 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1207
1224
1208 def __unicode__(self):
1225 def __unicode__(self):
1209 return u"<%s('%s:%s')>" % (
1226 return u"<%s('%s:%s')>" % (
1210 self.__class__.__name__, self.permission_id, self.permission_name
1227 self.__class__.__name__, self.permission_id, self.permission_name
1211 )
1228 )
1212
1229
1213 @classmethod
1230 @classmethod
1214 def get_by_key(cls, key):
1231 def get_by_key(cls, key):
1215 return cls.query().filter(cls.permission_name == key).scalar()
1232 return cls.query().filter(cls.permission_name == key).scalar()
1216
1233
1217 @classmethod
1234 @classmethod
1218 def get_default_perms(cls, default_user_id):
1235 def get_default_perms(cls, default_user_id):
1219 q = Session().query(UserRepoToPerm, Repository, cls)\
1236 q = Session().query(UserRepoToPerm, Repository, cls)\
1220 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1237 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1221 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1238 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1222 .filter(UserRepoToPerm.user_id == default_user_id)
1239 .filter(UserRepoToPerm.user_id == default_user_id)
1223
1240
1224 return q.all()
1241 return q.all()
1225
1242
1226 @classmethod
1243 @classmethod
1227 def get_default_group_perms(cls, default_user_id):
1244 def get_default_group_perms(cls, default_user_id):
1228 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1245 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1229 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1246 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1230 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1247 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1231 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1248 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1232
1249
1233 return q.all()
1250 return q.all()
1234
1251
1235
1252
1236 class UserRepoToPerm(Base, BaseModel):
1253 class UserRepoToPerm(Base, BaseModel):
1237 __tablename__ = 'repo_to_perm'
1254 __tablename__ = 'repo_to_perm'
1238 __table_args__ = (
1255 __table_args__ = (
1239 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1256 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1240 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1257 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1241 'mysql_charset': 'utf8'}
1258 'mysql_charset': 'utf8'}
1242 )
1259 )
1243 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1260 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1244 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1261 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1245 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1262 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1246 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1263 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1247
1264
1248 user = relationship('User')
1265 user = relationship('User')
1249 repository = relationship('Repository')
1266 repository = relationship('Repository')
1250 permission = relationship('Permission')
1267 permission = relationship('Permission')
1251
1268
1252 @classmethod
1269 @classmethod
1253 def create(cls, user, repository, permission):
1270 def create(cls, user, repository, permission):
1254 n = cls()
1271 n = cls()
1255 n.user = user
1272 n.user = user
1256 n.repository = repository
1273 n.repository = repository
1257 n.permission = permission
1274 n.permission = permission
1258 Session().add(n)
1275 Session().add(n)
1259 return n
1276 return n
1260
1277
1261 def __unicode__(self):
1278 def __unicode__(self):
1262 return u'<user:%s => %s >' % (self.user, self.repository)
1279 return u'<user:%s => %s >' % (self.user, self.repository)
1263
1280
1264
1281
1265 class UserToPerm(Base, BaseModel):
1282 class UserToPerm(Base, BaseModel):
1266 __tablename__ = 'user_to_perm'
1283 __tablename__ = 'user_to_perm'
1267 __table_args__ = (
1284 __table_args__ = (
1268 UniqueConstraint('user_id', 'permission_id'),
1285 UniqueConstraint('user_id', 'permission_id'),
1269 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1286 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1270 'mysql_charset': 'utf8'}
1287 'mysql_charset': 'utf8'}
1271 )
1288 )
1272 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1289 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1273 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1290 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1274 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1291 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1275
1292
1276 user = relationship('User')
1293 user = relationship('User')
1277 permission = relationship('Permission', lazy='joined')
1294 permission = relationship('Permission', lazy='joined')
1278
1295
1279
1296
1280 class UsersGroupRepoToPerm(Base, BaseModel):
1297 class UsersGroupRepoToPerm(Base, BaseModel):
1281 __tablename__ = 'users_group_repo_to_perm'
1298 __tablename__ = 'users_group_repo_to_perm'
1282 __table_args__ = (
1299 __table_args__ = (
1283 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1300 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1284 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1301 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1285 'mysql_charset': 'utf8'}
1302 'mysql_charset': 'utf8'}
1286 )
1303 )
1287 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1304 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1288 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1305 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1289 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1306 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1290 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1307 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1291
1308
1292 users_group = relationship('UsersGroup')
1309 users_group = relationship('UsersGroup')
1293 permission = relationship('Permission')
1310 permission = relationship('Permission')
1294 repository = relationship('Repository')
1311 repository = relationship('Repository')
1295
1312
1296 @classmethod
1313 @classmethod
1297 def create(cls, users_group, repository, permission):
1314 def create(cls, users_group, repository, permission):
1298 n = cls()
1315 n = cls()
1299 n.users_group = users_group
1316 n.users_group = users_group
1300 n.repository = repository
1317 n.repository = repository
1301 n.permission = permission
1318 n.permission = permission
1302 Session().add(n)
1319 Session().add(n)
1303 return n
1320 return n
1304
1321
1305 def __unicode__(self):
1322 def __unicode__(self):
1306 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1323 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1307
1324
1308
1325
1309 class UsersGroupToPerm(Base, BaseModel):
1326 class UsersGroupToPerm(Base, BaseModel):
1310 __tablename__ = 'users_group_to_perm'
1327 __tablename__ = 'users_group_to_perm'
1311 __table_args__ = (
1328 __table_args__ = (
1312 UniqueConstraint('users_group_id', 'permission_id',),
1329 UniqueConstraint('users_group_id', 'permission_id',),
1313 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1330 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1314 'mysql_charset': 'utf8'}
1331 'mysql_charset': 'utf8'}
1315 )
1332 )
1316 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1333 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1317 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1334 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1318 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1335 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1319
1336
1320 users_group = relationship('UsersGroup')
1337 users_group = relationship('UsersGroup')
1321 permission = relationship('Permission')
1338 permission = relationship('Permission')
1322
1339
1323
1340
1324 class UserRepoGroupToPerm(Base, BaseModel):
1341 class UserRepoGroupToPerm(Base, BaseModel):
1325 __tablename__ = 'user_repo_group_to_perm'
1342 __tablename__ = 'user_repo_group_to_perm'
1326 __table_args__ = (
1343 __table_args__ = (
1327 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1344 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1328 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1345 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1329 'mysql_charset': 'utf8'}
1346 'mysql_charset': 'utf8'}
1330 )
1347 )
1331
1348
1332 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1349 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1333 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1350 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1334 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1351 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1335 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1352 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1336
1353
1337 user = relationship('User')
1354 user = relationship('User')
1338 group = relationship('RepoGroup')
1355 group = relationship('RepoGroup')
1339 permission = relationship('Permission')
1356 permission = relationship('Permission')
1340
1357
1341
1358
1342 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1359 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1343 __tablename__ = 'users_group_repo_group_to_perm'
1360 __tablename__ = 'users_group_repo_group_to_perm'
1344 __table_args__ = (
1361 __table_args__ = (
1345 UniqueConstraint('users_group_id', 'group_id'),
1362 UniqueConstraint('users_group_id', 'group_id'),
1346 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1363 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1347 'mysql_charset': 'utf8'}
1364 'mysql_charset': 'utf8'}
1348 )
1365 )
1349
1366
1350 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)
1367 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)
1351 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1368 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1352 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1369 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1353 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1370 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1354
1371
1355 users_group = relationship('UsersGroup')
1372 users_group = relationship('UsersGroup')
1356 permission = relationship('Permission')
1373 permission = relationship('Permission')
1357 group = relationship('RepoGroup')
1374 group = relationship('RepoGroup')
1358
1375
1359
1376
1360 class Statistics(Base, BaseModel):
1377 class Statistics(Base, BaseModel):
1361 __tablename__ = 'statistics'
1378 __tablename__ = 'statistics'
1362 __table_args__ = (
1379 __table_args__ = (
1363 UniqueConstraint('repository_id'),
1380 UniqueConstraint('repository_id'),
1364 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1381 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1365 'mysql_charset': 'utf8'}
1382 'mysql_charset': 'utf8'}
1366 )
1383 )
1367 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1384 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1368 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1385 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1369 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1386 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1370 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1387 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1371 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1388 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1372 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1389 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1373
1390
1374 repository = relationship('Repository', single_parent=True)
1391 repository = relationship('Repository', single_parent=True)
1375
1392
1376
1393
1377 class UserFollowing(Base, BaseModel):
1394 class UserFollowing(Base, BaseModel):
1378 __tablename__ = 'user_followings'
1395 __tablename__ = 'user_followings'
1379 __table_args__ = (
1396 __table_args__ = (
1380 UniqueConstraint('user_id', 'follows_repository_id'),
1397 UniqueConstraint('user_id', 'follows_repository_id'),
1381 UniqueConstraint('user_id', 'follows_user_id'),
1398 UniqueConstraint('user_id', 'follows_user_id'),
1382 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1399 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1383 'mysql_charset': 'utf8'}
1400 'mysql_charset': 'utf8'}
1384 )
1401 )
1385
1402
1386 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1403 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1387 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1404 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1388 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1405 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1389 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1406 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1390 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1407 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1391
1408
1392 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1409 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1393
1410
1394 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1411 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1395 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1412 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1396
1413
1397 @classmethod
1414 @classmethod
1398 def get_repo_followers(cls, repo_id):
1415 def get_repo_followers(cls, repo_id):
1399 return cls.query().filter(cls.follows_repo_id == repo_id)
1416 return cls.query().filter(cls.follows_repo_id == repo_id)
1400
1417
1401
1418
1402 class CacheInvalidation(Base, BaseModel):
1419 class CacheInvalidation(Base, BaseModel):
1403 __tablename__ = 'cache_invalidation'
1420 __tablename__ = 'cache_invalidation'
1404 __table_args__ = (
1421 __table_args__ = (
1405 UniqueConstraint('cache_key'),
1422 UniqueConstraint('cache_key'),
1406 Index('key_idx', 'cache_key'),
1423 Index('key_idx', 'cache_key'),
1407 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1424 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1408 'mysql_charset': 'utf8'},
1425 'mysql_charset': 'utf8'},
1409 )
1426 )
1410 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1427 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1411 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1428 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1412 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1429 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1413 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1430 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1414
1431
1415 def __init__(self, cache_key, cache_args=''):
1432 def __init__(self, cache_key, cache_args=''):
1416 self.cache_key = cache_key
1433 self.cache_key = cache_key
1417 self.cache_args = cache_args
1434 self.cache_args = cache_args
1418 self.cache_active = False
1435 self.cache_active = False
1419
1436
1420 def __unicode__(self):
1437 def __unicode__(self):
1421 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1438 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1422 self.cache_id, self.cache_key)
1439 self.cache_id, self.cache_key)
1423
1440
1424 @property
1441 @property
1425 def prefix(self):
1442 def prefix(self):
1426 _split = self.cache_key.split(self.cache_args, 1)
1443 _split = self.cache_key.split(self.cache_args, 1)
1427 if _split and len(_split) == 2:
1444 if _split and len(_split) == 2:
1428 return _split[0]
1445 return _split[0]
1429 return ''
1446 return ''
1430
1447
1431 @classmethod
1448 @classmethod
1432 def clear_cache(cls):
1449 def clear_cache(cls):
1433 cls.query().delete()
1450 cls.query().delete()
1434
1451
1435 @classmethod
1452 @classmethod
1436 def _get_key(cls, key):
1453 def _get_key(cls, key):
1437 """
1454 """
1438 Wrapper for generating a key, together with a prefix
1455 Wrapper for generating a key, together with a prefix
1439
1456
1440 :param key:
1457 :param key:
1441 """
1458 """
1442 import rhodecode
1459 import rhodecode
1443 prefix = ''
1460 prefix = ''
1444 org_key = key
1461 org_key = key
1445 iid = rhodecode.CONFIG.get('instance_id')
1462 iid = rhodecode.CONFIG.get('instance_id')
1446 if iid:
1463 if iid:
1447 prefix = iid
1464 prefix = iid
1448
1465
1449 return "%s%s" % (prefix, key), prefix, org_key
1466 return "%s%s" % (prefix, key), prefix, org_key
1450
1467
1451 @classmethod
1468 @classmethod
1452 def get_by_key(cls, key):
1469 def get_by_key(cls, key):
1453 return cls.query().filter(cls.cache_key == key).scalar()
1470 return cls.query().filter(cls.cache_key == key).scalar()
1454
1471
1455 @classmethod
1472 @classmethod
1456 def get_by_repo_name(cls, repo_name):
1473 def get_by_repo_name(cls, repo_name):
1457 return cls.query().filter(cls.cache_args == repo_name).all()
1474 return cls.query().filter(cls.cache_args == repo_name).all()
1458
1475
1459 @classmethod
1476 @classmethod
1460 def _get_or_create_key(cls, key, repo_name, commit=True):
1477 def _get_or_create_key(cls, key, repo_name, commit=True):
1461 inv_obj = Session().query(cls).filter(cls.cache_key == key).scalar()
1478 inv_obj = Session().query(cls).filter(cls.cache_key == key).scalar()
1462 if not inv_obj:
1479 if not inv_obj:
1463 try:
1480 try:
1464 inv_obj = CacheInvalidation(key, repo_name)
1481 inv_obj = CacheInvalidation(key, repo_name)
1465 Session().add(inv_obj)
1482 Session().add(inv_obj)
1466 if commit:
1483 if commit:
1467 Session().commit()
1484 Session().commit()
1468 except Exception:
1485 except Exception:
1469 log.error(traceback.format_exc())
1486 log.error(traceback.format_exc())
1470 Session().rollback()
1487 Session().rollback()
1471 return inv_obj
1488 return inv_obj
1472
1489
1473 @classmethod
1490 @classmethod
1474 def invalidate(cls, key):
1491 def invalidate(cls, key):
1475 """
1492 """
1476 Returns Invalidation object if this given key should be invalidated
1493 Returns Invalidation object if this given key should be invalidated
1477 None otherwise. `cache_active = False` means that this cache
1494 None otherwise. `cache_active = False` means that this cache
1478 state is not valid and needs to be invalidated
1495 state is not valid and needs to be invalidated
1479
1496
1480 :param key:
1497 :param key:
1481 """
1498 """
1482 repo_name = key
1499 repo_name = key
1483 repo_name = remove_suffix(repo_name, '_README')
1500 repo_name = remove_suffix(repo_name, '_README')
1484 repo_name = remove_suffix(repo_name, '_RSS')
1501 repo_name = remove_suffix(repo_name, '_RSS')
1485 repo_name = remove_suffix(repo_name, '_ATOM')
1502 repo_name = remove_suffix(repo_name, '_ATOM')
1486
1503
1487 # adds instance prefix
1504 # adds instance prefix
1488 key, _prefix, _org_key = cls._get_key(key)
1505 key, _prefix, _org_key = cls._get_key(key)
1489 inv = cls._get_or_create_key(key, repo_name)
1506 inv = cls._get_or_create_key(key, repo_name)
1490
1507
1491 if inv and inv.cache_active is False:
1508 if inv and inv.cache_active is False:
1492 return inv
1509 return inv
1493
1510
1494 @classmethod
1511 @classmethod
1495 def set_invalidate(cls, key=None, repo_name=None):
1512 def set_invalidate(cls, key=None, repo_name=None):
1496 """
1513 """
1497 Mark this Cache key for invalidation, either by key or whole
1514 Mark this Cache key for invalidation, either by key or whole
1498 cache sets based on repo_name
1515 cache sets based on repo_name
1499
1516
1500 :param key:
1517 :param key:
1501 """
1518 """
1502 if key:
1519 if key:
1503 key, _prefix, _org_key = cls._get_key(key)
1520 key, _prefix, _org_key = cls._get_key(key)
1504 inv_objs = Session().query(cls).filter(cls.cache_key == key).all()
1521 inv_objs = Session().query(cls).filter(cls.cache_key == key).all()
1505 elif repo_name:
1522 elif repo_name:
1506 inv_objs = Session().query(cls).filter(cls.cache_args == repo_name).all()
1523 inv_objs = Session().query(cls).filter(cls.cache_args == repo_name).all()
1507
1524
1508 log.debug('marking %s key[s] for invalidation based on key=%s,repo_name=%s'
1525 log.debug('marking %s key[s] for invalidation based on key=%s,repo_name=%s'
1509 % (len(inv_objs), key, repo_name))
1526 % (len(inv_objs), key, repo_name))
1510 try:
1527 try:
1511 for inv_obj in inv_objs:
1528 for inv_obj in inv_objs:
1512 inv_obj.cache_active = False
1529 inv_obj.cache_active = False
1513 Session().add(inv_obj)
1530 Session().add(inv_obj)
1514 Session().commit()
1531 Session().commit()
1515 except Exception:
1532 except Exception:
1516 log.error(traceback.format_exc())
1533 log.error(traceback.format_exc())
1517 Session().rollback()
1534 Session().rollback()
1518
1535
1519 @classmethod
1536 @classmethod
1520 def set_valid(cls, key):
1537 def set_valid(cls, key):
1521 """
1538 """
1522 Mark this cache key as active and currently cached
1539 Mark this cache key as active and currently cached
1523
1540
1524 :param key:
1541 :param key:
1525 """
1542 """
1526 inv_obj = cls.get_by_key(key)
1543 inv_obj = cls.get_by_key(key)
1527 inv_obj.cache_active = True
1544 inv_obj.cache_active = True
1528 Session().add(inv_obj)
1545 Session().add(inv_obj)
1529 Session().commit()
1546 Session().commit()
1530
1547
1531 @classmethod
1548 @classmethod
1532 def get_cache_map(cls):
1549 def get_cache_map(cls):
1533
1550
1534 class cachemapdict(dict):
1551 class cachemapdict(dict):
1535
1552
1536 def __init__(self, *args, **kwargs):
1553 def __init__(self, *args, **kwargs):
1537 fixkey = kwargs.get('fixkey')
1554 fixkey = kwargs.get('fixkey')
1538 if fixkey:
1555 if fixkey:
1539 del kwargs['fixkey']
1556 del kwargs['fixkey']
1540 self.fixkey = fixkey
1557 self.fixkey = fixkey
1541 super(cachemapdict, self).__init__(*args, **kwargs)
1558 super(cachemapdict, self).__init__(*args, **kwargs)
1542
1559
1543 def __getattr__(self, name):
1560 def __getattr__(self, name):
1544 key = name
1561 key = name
1545 if self.fixkey:
1562 if self.fixkey:
1546 key, _prefix, _org_key = cls._get_key(key)
1563 key, _prefix, _org_key = cls._get_key(key)
1547 if key in self.__dict__:
1564 if key in self.__dict__:
1548 return self.__dict__[key]
1565 return self.__dict__[key]
1549 else:
1566 else:
1550 return self[key]
1567 return self[key]
1551
1568
1552 def __getitem__(self, key):
1569 def __getitem__(self, key):
1553 if self.fixkey:
1570 if self.fixkey:
1554 key, _prefix, _org_key = cls._get_key(key)
1571 key, _prefix, _org_key = cls._get_key(key)
1555 try:
1572 try:
1556 return super(cachemapdict, self).__getitem__(key)
1573 return super(cachemapdict, self).__getitem__(key)
1557 except KeyError:
1574 except KeyError:
1558 return
1575 return
1559
1576
1560 cache_map = cachemapdict(fixkey=True)
1577 cache_map = cachemapdict(fixkey=True)
1561 for obj in cls.query().all():
1578 for obj in cls.query().all():
1562 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1579 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1563 return cache_map
1580 return cache_map
1564
1581
1565
1582
1566 class ChangesetComment(Base, BaseModel):
1583 class ChangesetComment(Base, BaseModel):
1567 __tablename__ = 'changeset_comments'
1584 __tablename__ = 'changeset_comments'
1568 __table_args__ = (
1585 __table_args__ = (
1569 Index('cc_revision_idx', 'revision'),
1586 Index('cc_revision_idx', 'revision'),
1570 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1587 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1571 'mysql_charset': 'utf8'},
1588 'mysql_charset': 'utf8'},
1572 )
1589 )
1573 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1590 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1574 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1591 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1575 revision = Column('revision', String(40), nullable=True)
1592 revision = Column('revision', String(40), nullable=True)
1576 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1593 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1577 line_no = Column('line_no', Unicode(10), nullable=True)
1594 line_no = Column('line_no', Unicode(10), nullable=True)
1578 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1595 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1579 f_path = Column('f_path', Unicode(1000), nullable=True)
1596 f_path = Column('f_path', Unicode(1000), nullable=True)
1580 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1597 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1581 text = Column('text', UnicodeText(25000), nullable=False)
1598 text = Column('text', UnicodeText(25000), nullable=False)
1582 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1599 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1583 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1600 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1584
1601
1585 author = relationship('User', lazy='joined')
1602 author = relationship('User', lazy='joined')
1586 repo = relationship('Repository')
1603 repo = relationship('Repository')
1587 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1604 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1588 pull_request = relationship('PullRequest', lazy='joined')
1605 pull_request = relationship('PullRequest', lazy='joined')
1589
1606
1590 @classmethod
1607 @classmethod
1591 def get_users(cls, revision=None, pull_request_id=None):
1608 def get_users(cls, revision=None, pull_request_id=None):
1592 """
1609 """
1593 Returns user associated with this ChangesetComment. ie those
1610 Returns user associated with this ChangesetComment. ie those
1594 who actually commented
1611 who actually commented
1595
1612
1596 :param cls:
1613 :param cls:
1597 :param revision:
1614 :param revision:
1598 """
1615 """
1599 q = Session().query(User)\
1616 q = Session().query(User)\
1600 .join(ChangesetComment.author)
1617 .join(ChangesetComment.author)
1601 if revision:
1618 if revision:
1602 q = q.filter(cls.revision == revision)
1619 q = q.filter(cls.revision == revision)
1603 elif pull_request_id:
1620 elif pull_request_id:
1604 q = q.filter(cls.pull_request_id == pull_request_id)
1621 q = q.filter(cls.pull_request_id == pull_request_id)
1605 return q.all()
1622 return q.all()
1606
1623
1607
1624
1608 class ChangesetStatus(Base, BaseModel):
1625 class ChangesetStatus(Base, BaseModel):
1609 __tablename__ = 'changeset_statuses'
1626 __tablename__ = 'changeset_statuses'
1610 __table_args__ = (
1627 __table_args__ = (
1611 Index('cs_revision_idx', 'revision'),
1628 Index('cs_revision_idx', 'revision'),
1612 Index('cs_version_idx', 'version'),
1629 Index('cs_version_idx', 'version'),
1613 UniqueConstraint('repo_id', 'revision', 'version'),
1630 UniqueConstraint('repo_id', 'revision', 'version'),
1614 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1631 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1615 'mysql_charset': 'utf8'}
1632 'mysql_charset': 'utf8'}
1616 )
1633 )
1617 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1634 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1618 STATUS_APPROVED = 'approved'
1635 STATUS_APPROVED = 'approved'
1619 STATUS_REJECTED = 'rejected'
1636 STATUS_REJECTED = 'rejected'
1620 STATUS_UNDER_REVIEW = 'under_review'
1637 STATUS_UNDER_REVIEW = 'under_review'
1621
1638
1622 STATUSES = [
1639 STATUSES = [
1623 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1640 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1624 (STATUS_APPROVED, _("Approved")),
1641 (STATUS_APPROVED, _("Approved")),
1625 (STATUS_REJECTED, _("Rejected")),
1642 (STATUS_REJECTED, _("Rejected")),
1626 (STATUS_UNDER_REVIEW, _("Under Review")),
1643 (STATUS_UNDER_REVIEW, _("Under Review")),
1627 ]
1644 ]
1628
1645
1629 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1646 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1630 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1647 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1631 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1648 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1632 revision = Column('revision', String(40), nullable=False)
1649 revision = Column('revision', String(40), nullable=False)
1633 status = Column('status', String(128), nullable=False, default=DEFAULT)
1650 status = Column('status', String(128), nullable=False, default=DEFAULT)
1634 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1651 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1635 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1652 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1636 version = Column('version', Integer(), nullable=False, default=0)
1653 version = Column('version', Integer(), nullable=False, default=0)
1637 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1654 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1638
1655
1639 author = relationship('User', lazy='joined')
1656 author = relationship('User', lazy='joined')
1640 repo = relationship('Repository')
1657 repo = relationship('Repository')
1641 comment = relationship('ChangesetComment', lazy='joined')
1658 comment = relationship('ChangesetComment', lazy='joined')
1642 pull_request = relationship('PullRequest', lazy='joined')
1659 pull_request = relationship('PullRequest', lazy='joined')
1643
1660
1644 def __unicode__(self):
1661 def __unicode__(self):
1645 return u"<%s('%s:%s')>" % (
1662 return u"<%s('%s:%s')>" % (
1646 self.__class__.__name__,
1663 self.__class__.__name__,
1647 self.status, self.author
1664 self.status, self.author
1648 )
1665 )
1649
1666
1650 @classmethod
1667 @classmethod
1651 def get_status_lbl(cls, value):
1668 def get_status_lbl(cls, value):
1652 return dict(cls.STATUSES).get(value)
1669 return dict(cls.STATUSES).get(value)
1653
1670
1654 @property
1671 @property
1655 def status_lbl(self):
1672 def status_lbl(self):
1656 return ChangesetStatus.get_status_lbl(self.status)
1673 return ChangesetStatus.get_status_lbl(self.status)
1657
1674
1658
1675
1659 class PullRequest(Base, BaseModel):
1676 class PullRequest(Base, BaseModel):
1660 __tablename__ = 'pull_requests'
1677 __tablename__ = 'pull_requests'
1661 __table_args__ = (
1678 __table_args__ = (
1662 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1679 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1663 'mysql_charset': 'utf8'},
1680 'mysql_charset': 'utf8'},
1664 )
1681 )
1665
1682
1666 STATUS_NEW = u'new'
1683 STATUS_NEW = u'new'
1667 STATUS_OPEN = u'open'
1684 STATUS_OPEN = u'open'
1668 STATUS_CLOSED = u'closed'
1685 STATUS_CLOSED = u'closed'
1669
1686
1670 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1687 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1671 title = Column('title', Unicode(256), nullable=True)
1688 title = Column('title', Unicode(256), nullable=True)
1672 description = Column('description', UnicodeText(10240), nullable=True)
1689 description = Column('description', UnicodeText(10240), nullable=True)
1673 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1690 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1674 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1691 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1675 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1692 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1676 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1693 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1677 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1694 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1678 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1695 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1679 org_ref = Column('org_ref', Unicode(256), nullable=False)
1696 org_ref = Column('org_ref', Unicode(256), nullable=False)
1680 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1697 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1681 other_ref = Column('other_ref', Unicode(256), nullable=False)
1698 other_ref = Column('other_ref', Unicode(256), nullable=False)
1682
1699
1683 @hybrid_property
1700 @hybrid_property
1684 def revisions(self):
1701 def revisions(self):
1685 return self._revisions.split(':')
1702 return self._revisions.split(':')
1686
1703
1687 @revisions.setter
1704 @revisions.setter
1688 def revisions(self, val):
1705 def revisions(self, val):
1689 self._revisions = ':'.join(val)
1706 self._revisions = ':'.join(val)
1690
1707
1691 author = relationship('User', lazy='joined')
1708 author = relationship('User', lazy='joined')
1692 reviewers = relationship('PullRequestReviewers',
1709 reviewers = relationship('PullRequestReviewers',
1693 cascade="all, delete, delete-orphan")
1710 cascade="all, delete, delete-orphan")
1694 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1711 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1695 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1712 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1696 statuses = relationship('ChangesetStatus')
1713 statuses = relationship('ChangesetStatus')
1697 comments = relationship('ChangesetComment',
1714 comments = relationship('ChangesetComment',
1698 cascade="all, delete, delete-orphan")
1715 cascade="all, delete, delete-orphan")
1699
1716
1700 def is_closed(self):
1717 def is_closed(self):
1701 return self.status == self.STATUS_CLOSED
1718 return self.status == self.STATUS_CLOSED
1702
1719
1703 def __json__(self):
1720 def __json__(self):
1704 return dict(
1721 return dict(
1705 revisions=self.revisions
1722 revisions=self.revisions
1706 )
1723 )
1707
1724
1708
1725
1709 class PullRequestReviewers(Base, BaseModel):
1726 class PullRequestReviewers(Base, BaseModel):
1710 __tablename__ = 'pull_request_reviewers'
1727 __tablename__ = 'pull_request_reviewers'
1711 __table_args__ = (
1728 __table_args__ = (
1712 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1729 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1713 'mysql_charset': 'utf8'},
1730 'mysql_charset': 'utf8'},
1714 )
1731 )
1715
1732
1716 def __init__(self, user=None, pull_request=None):
1733 def __init__(self, user=None, pull_request=None):
1717 self.user = user
1734 self.user = user
1718 self.pull_request = pull_request
1735 self.pull_request = pull_request
1719
1736
1720 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1737 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1721 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1738 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1722 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1739 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1723
1740
1724 user = relationship('User')
1741 user = relationship('User')
1725 pull_request = relationship('PullRequest')
1742 pull_request = relationship('PullRequest')
1726
1743
1727
1744
1728 class Notification(Base, BaseModel):
1745 class Notification(Base, BaseModel):
1729 __tablename__ = 'notifications'
1746 __tablename__ = 'notifications'
1730 __table_args__ = (
1747 __table_args__ = (
1731 Index('notification_type_idx', 'type'),
1748 Index('notification_type_idx', 'type'),
1732 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1749 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1733 'mysql_charset': 'utf8'},
1750 'mysql_charset': 'utf8'},
1734 )
1751 )
1735
1752
1736 TYPE_CHANGESET_COMMENT = u'cs_comment'
1753 TYPE_CHANGESET_COMMENT = u'cs_comment'
1737 TYPE_MESSAGE = u'message'
1754 TYPE_MESSAGE = u'message'
1738 TYPE_MENTION = u'mention'
1755 TYPE_MENTION = u'mention'
1739 TYPE_REGISTRATION = u'registration'
1756 TYPE_REGISTRATION = u'registration'
1740 TYPE_PULL_REQUEST = u'pull_request'
1757 TYPE_PULL_REQUEST = u'pull_request'
1741 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1758 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1742
1759
1743 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1760 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1744 subject = Column('subject', Unicode(512), nullable=True)
1761 subject = Column('subject', Unicode(512), nullable=True)
1745 body = Column('body', UnicodeText(50000), nullable=True)
1762 body = Column('body', UnicodeText(50000), nullable=True)
1746 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1763 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1747 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1764 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1748 type_ = Column('type', Unicode(256))
1765 type_ = Column('type', Unicode(256))
1749
1766
1750 created_by_user = relationship('User')
1767 created_by_user = relationship('User')
1751 notifications_to_users = relationship('UserNotification', lazy='joined',
1768 notifications_to_users = relationship('UserNotification', lazy='joined',
1752 cascade="all, delete, delete-orphan")
1769 cascade="all, delete, delete-orphan")
1753
1770
1754 @property
1771 @property
1755 def recipients(self):
1772 def recipients(self):
1756 return [x.user for x in UserNotification.query()\
1773 return [x.user for x in UserNotification.query()\
1757 .filter(UserNotification.notification == self)\
1774 .filter(UserNotification.notification == self)\
1758 .order_by(UserNotification.user_id.asc()).all()]
1775 .order_by(UserNotification.user_id.asc()).all()]
1759
1776
1760 @classmethod
1777 @classmethod
1761 def create(cls, created_by, subject, body, recipients, type_=None):
1778 def create(cls, created_by, subject, body, recipients, type_=None):
1762 if type_ is None:
1779 if type_ is None:
1763 type_ = Notification.TYPE_MESSAGE
1780 type_ = Notification.TYPE_MESSAGE
1764
1781
1765 notification = cls()
1782 notification = cls()
1766 notification.created_by_user = created_by
1783 notification.created_by_user = created_by
1767 notification.subject = subject
1784 notification.subject = subject
1768 notification.body = body
1785 notification.body = body
1769 notification.type_ = type_
1786 notification.type_ = type_
1770 notification.created_on = datetime.datetime.now()
1787 notification.created_on = datetime.datetime.now()
1771
1788
1772 for u in recipients:
1789 for u in recipients:
1773 assoc = UserNotification()
1790 assoc = UserNotification()
1774 assoc.notification = notification
1791 assoc.notification = notification
1775 u.notifications.append(assoc)
1792 u.notifications.append(assoc)
1776 Session().add(notification)
1793 Session().add(notification)
1777 return notification
1794 return notification
1778
1795
1779 @property
1796 @property
1780 def description(self):
1797 def description(self):
1781 from rhodecode.model.notification import NotificationModel
1798 from rhodecode.model.notification import NotificationModel
1782 return NotificationModel().make_description(self)
1799 return NotificationModel().make_description(self)
1783
1800
1784
1801
1785 class UserNotification(Base, BaseModel):
1802 class UserNotification(Base, BaseModel):
1786 __tablename__ = 'user_to_notification'
1803 __tablename__ = 'user_to_notification'
1787 __table_args__ = (
1804 __table_args__ = (
1788 UniqueConstraint('user_id', 'notification_id'),
1805 UniqueConstraint('user_id', 'notification_id'),
1789 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1806 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1790 'mysql_charset': 'utf8'}
1807 'mysql_charset': 'utf8'}
1791 )
1808 )
1792 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1809 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1793 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1810 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1794 read = Column('read', Boolean, default=False)
1811 read = Column('read', Boolean, default=False)
1795 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1812 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1796
1813
1797 user = relationship('User', lazy="joined")
1814 user = relationship('User', lazy="joined")
1798 notification = relationship('Notification', lazy="joined",
1815 notification = relationship('Notification', lazy="joined",
1799 order_by=lambda: Notification.created_on.desc(),)
1816 order_by=lambda: Notification.created_on.desc(),)
1800
1817
1801 def mark_as_read(self):
1818 def mark_as_read(self):
1802 self.read = True
1819 self.read = True
1803 Session().add(self)
1820 Session().add(self)
1804
1821
1805
1822
1806 class DbMigrateVersion(Base, BaseModel):
1823 class DbMigrateVersion(Base, BaseModel):
1807 __tablename__ = 'db_migrate_version'
1824 __tablename__ = 'db_migrate_version'
1808 __table_args__ = (
1825 __table_args__ = (
1809 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1826 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1810 'mysql_charset': 'utf8'},
1827 'mysql_charset': 'utf8'},
1811 )
1828 )
1812 repository_id = Column('repository_id', String(250), primary_key=True)
1829 repository_id = Column('repository_id', String(250), primary_key=True)
1813 repository_path = Column('repository_path', Text)
1830 repository_path = Column('repository_path', Text)
1814 version = Column('version', Integer)
1831 version = Column('version', Integer)
@@ -1,352 +1,364 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 enable_locking = v.StringBoolean(if_missing=False)
130 enable_locking = v.StringBoolean(if_missing=False)
131 recursive = v.StringBoolean(if_missing=False)
131 recursive = v.StringBoolean(if_missing=False)
132 chained_validators = [v.ValidReposGroup(edit, old_data),
132 chained_validators = [v.ValidReposGroup(edit, old_data),
133 v.ValidPerms('group')]
133 v.ValidPerms('group')]
134
134
135 return _ReposGroupForm
135 return _ReposGroupForm
136
136
137
137
138 def RegisterForm(edit=False, old_data={}):
138 def RegisterForm(edit=False, old_data={}):
139 class _RegisterForm(formencode.Schema):
139 class _RegisterForm(formencode.Schema):
140 allow_extra_fields = True
140 allow_extra_fields = True
141 filter_extra_fields = True
141 filter_extra_fields = True
142 username = All(
142 username = All(
143 v.ValidUsername(edit, old_data),
143 v.ValidUsername(edit, old_data),
144 v.UnicodeString(strip=True, min=1, not_empty=True)
144 v.UnicodeString(strip=True, min=1, not_empty=True)
145 )
145 )
146 password = All(
146 password = All(
147 v.ValidPassword(),
147 v.ValidPassword(),
148 v.UnicodeString(strip=False, min=6, not_empty=True)
148 v.UnicodeString(strip=False, min=6, not_empty=True)
149 )
149 )
150 password_confirmation = All(
150 password_confirmation = All(
151 v.ValidPassword(),
151 v.ValidPassword(),
152 v.UnicodeString(strip=False, min=6, not_empty=True)
152 v.UnicodeString(strip=False, min=6, not_empty=True)
153 )
153 )
154 active = v.StringBoolean(if_missing=False)
154 active = v.StringBoolean(if_missing=False)
155 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
155 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
156 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
156 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
157 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
157 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
158
158
159 chained_validators = [v.ValidPasswordsMatch()]
159 chained_validators = [v.ValidPasswordsMatch()]
160
160
161 return _RegisterForm
161 return _RegisterForm
162
162
163
163
164 def PasswordResetForm():
164 def PasswordResetForm():
165 class _PasswordResetForm(formencode.Schema):
165 class _PasswordResetForm(formencode.Schema):
166 allow_extra_fields = True
166 allow_extra_fields = True
167 filter_extra_fields = True
167 filter_extra_fields = True
168 email = All(v.ValidSystemEmail(), v.Email(not_empty=True))
168 email = All(v.ValidSystemEmail(), v.Email(not_empty=True))
169 return _PasswordResetForm
169 return _PasswordResetForm
170
170
171
171
172 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
172 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
173 repo_groups=[], landing_revs=[]):
173 repo_groups=[], landing_revs=[]):
174 class _RepoForm(formencode.Schema):
174 class _RepoForm(formencode.Schema):
175 allow_extra_fields = True
175 allow_extra_fields = True
176 filter_extra_fields = False
176 filter_extra_fields = True
177 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
177 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
178 v.SlugifyName())
178 v.SlugifyName())
179 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
180 repo_group = All(v.CanWriteGroup(),
179 repo_group = All(v.CanWriteGroup(),
181 v.OneOf(repo_groups, hideList=True))
180 v.OneOf(repo_groups, hideList=True))
182 repo_type = v.OneOf(supported_backends)
181 repo_type = v.OneOf(supported_backends)
183 description = v.UnicodeString(strip=True, min=1, not_empty=False)
182 repo_description = v.UnicodeString(strip=True, min=1, not_empty=False)
184 private = v.StringBoolean(if_missing=False)
183 repo_private = v.StringBoolean(if_missing=False)
185 enable_statistics = v.StringBoolean(if_missing=False)
184 repo_enable_statistics = v.StringBoolean(if_missing=False)
186 enable_downloads = v.StringBoolean(if_missing=False)
185 repo_enable_downloads = v.StringBoolean(if_missing=False)
187 enable_locking = v.StringBoolean(if_missing=False)
186 repo_enable_locking = v.StringBoolean(if_missing=False)
188 landing_rev = v.OneOf(landing_revs, hideList=True)
187 repo_landing_rev = v.OneOf(landing_revs, hideList=True)
188 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
189
189
190 if edit:
190 if edit:
191 #this is repo owner
191 #this is repo owner
192 user = All(v.UnicodeString(not_empty=True), v.ValidRepoUser())
192 user = All(v.UnicodeString(not_empty=True), v.ValidRepoUser())
193
193
194 chained_validators = [v.ValidCloneUri(),
194 chained_validators = [v.ValidCloneUri(),
195 v.ValidRepoName(edit, old_data),
195 v.ValidRepoName(edit, old_data),
196 v.ValidPerms()]
196 v.ValidPerms()]
197 return _RepoForm
197 return _RepoForm
198
198
199
199
200 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
200 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
201 repo_groups=[], landing_revs=[]):
201 repo_groups=[], landing_revs=[]):
202 class _RepoForkForm(formencode.Schema):
202 class _RepoForkForm(formencode.Schema):
203 allow_extra_fields = True
203 allow_extra_fields = True
204 filter_extra_fields = False
204 filter_extra_fields = False
205 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
205 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
206 v.SlugifyName())
206 v.SlugifyName())
207 repo_group = All(v.CanWriteGroup(),
207 repo_group = All(v.CanWriteGroup(),
208 v.OneOf(repo_groups, hideList=True))
208 v.OneOf(repo_groups, hideList=True))
209 repo_type = All(v.ValidForkType(old_data), v.OneOf(supported_backends))
209 repo_type = All(v.ValidForkType(old_data), v.OneOf(supported_backends))
210 description = v.UnicodeString(strip=True, min=1, not_empty=True)
210 description = v.UnicodeString(strip=True, min=1, not_empty=True)
211 private = v.StringBoolean(if_missing=False)
211 private = v.StringBoolean(if_missing=False)
212 copy_permissions = v.StringBoolean(if_missing=False)
212 copy_permissions = v.StringBoolean(if_missing=False)
213 update_after_clone = v.StringBoolean(if_missing=False)
213 update_after_clone = v.StringBoolean(if_missing=False)
214 fork_parent_id = v.UnicodeString()
214 fork_parent_id = v.UnicodeString()
215 chained_validators = [v.ValidForkName(edit, old_data)]
215 chained_validators = [v.ValidForkName(edit, old_data)]
216 landing_rev = v.OneOf(landing_revs, hideList=True)
216 landing_rev = v.OneOf(landing_revs, hideList=True)
217
217
218 return _RepoForkForm
218 return _RepoForkForm
219
219
220
220
221 def RepoSettingsForm(edit=False, old_data={},
221 def RepoSettingsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
222 supported_backends=BACKENDS.keys(), repo_groups=[],
222 repo_groups=[], landing_revs=[]):
223 landing_revs=[]):
224 class _RepoForm(formencode.Schema):
223 class _RepoForm(formencode.Schema):
225 allow_extra_fields = True
224 allow_extra_fields = True
226 filter_extra_fields = False
225 filter_extra_fields = False
227 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
226 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
228 v.SlugifyName())
227 v.SlugifyName())
229 description = v.UnicodeString(strip=True, min=1, not_empty=True)
228 description = v.UnicodeString(strip=True, min=1, not_empty=True)
230 repo_group = All(v.CanWriteGroup(),
229 repo_group = All(v.CanWriteGroup(),
231 v.OneOf(repo_groups, hideList=True))
230 v.OneOf(repo_groups, hideList=True))
232 private = v.StringBoolean(if_missing=False)
231 private = v.StringBoolean(if_missing=False)
233 landing_rev = v.OneOf(landing_revs, hideList=True)
232 landing_rev = v.OneOf(landing_revs, hideList=True)
234 chained_validators = [v.ValidRepoName(edit, old_data), v.ValidPerms(),
233 chained_validators = [v.ValidRepoName(edit, old_data), v.ValidPerms(),
235 v.ValidSettings()]
234 v.ValidSettings()]
236 return _RepoForm
235 return _RepoForm
237
236
238
237
239 def ApplicationSettingsForm():
238 def ApplicationSettingsForm():
240 class _ApplicationSettingsForm(formencode.Schema):
239 class _ApplicationSettingsForm(formencode.Schema):
241 allow_extra_fields = True
240 allow_extra_fields = True
242 filter_extra_fields = False
241 filter_extra_fields = False
243 rhodecode_title = v.UnicodeString(strip=True, min=1, not_empty=True)
242 rhodecode_title = v.UnicodeString(strip=True, min=1, not_empty=True)
244 rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True)
243 rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True)
245 rhodecode_ga_code = v.UnicodeString(strip=True, min=1, not_empty=False)
244 rhodecode_ga_code = v.UnicodeString(strip=True, min=1, not_empty=False)
246
245
247 return _ApplicationSettingsForm
246 return _ApplicationSettingsForm
248
247
249
248
250 def ApplicationVisualisationForm():
249 def ApplicationVisualisationForm():
251 class _ApplicationVisualisationForm(formencode.Schema):
250 class _ApplicationVisualisationForm(formencode.Schema):
252 allow_extra_fields = True
251 allow_extra_fields = True
253 filter_extra_fields = False
252 filter_extra_fields = False
254 rhodecode_show_public_icon = v.StringBoolean(if_missing=False)
253 rhodecode_show_public_icon = v.StringBoolean(if_missing=False)
255 rhodecode_show_private_icon = v.StringBoolean(if_missing=False)
254 rhodecode_show_private_icon = v.StringBoolean(if_missing=False)
256 rhodecode_stylify_metatags = v.StringBoolean(if_missing=False)
255 rhodecode_stylify_metatags = v.StringBoolean(if_missing=False)
257
256
258 rhodecode_lightweight_dashboard = v.StringBoolean(if_missing=False)
257 rhodecode_lightweight_dashboard = v.StringBoolean(if_missing=False)
259 rhodecode_lightweight_journal = v.StringBoolean(if_missing=False)
258 rhodecode_lightweight_journal = v.StringBoolean(if_missing=False)
260
259
261 return _ApplicationVisualisationForm
260 return _ApplicationVisualisationForm
262
261
263
262
264 def ApplicationUiSettingsForm():
263 def ApplicationUiSettingsForm():
265 class _ApplicationUiSettingsForm(formencode.Schema):
264 class _ApplicationUiSettingsForm(formencode.Schema):
266 allow_extra_fields = True
265 allow_extra_fields = True
267 filter_extra_fields = False
266 filter_extra_fields = False
268 web_push_ssl = v.StringBoolean(if_missing=False)
267 web_push_ssl = v.StringBoolean(if_missing=False)
269 paths_root_path = All(
268 paths_root_path = All(
270 v.ValidPath(),
269 v.ValidPath(),
271 v.UnicodeString(strip=True, min=1, not_empty=True)
270 v.UnicodeString(strip=True, min=1, not_empty=True)
272 )
271 )
273 hooks_changegroup_update = v.StringBoolean(if_missing=False)
272 hooks_changegroup_update = v.StringBoolean(if_missing=False)
274 hooks_changegroup_repo_size = v.StringBoolean(if_missing=False)
273 hooks_changegroup_repo_size = v.StringBoolean(if_missing=False)
275 hooks_changegroup_push_logger = v.StringBoolean(if_missing=False)
274 hooks_changegroup_push_logger = v.StringBoolean(if_missing=False)
276 hooks_outgoing_pull_logger = v.StringBoolean(if_missing=False)
275 hooks_outgoing_pull_logger = v.StringBoolean(if_missing=False)
277
276
278 extensions_largefiles = v.StringBoolean(if_missing=False)
277 extensions_largefiles = v.StringBoolean(if_missing=False)
279 extensions_hgsubversion = v.StringBoolean(if_missing=False)
278 extensions_hgsubversion = v.StringBoolean(if_missing=False)
280 extensions_hggit = v.StringBoolean(if_missing=False)
279 extensions_hggit = v.StringBoolean(if_missing=False)
281
280
282 return _ApplicationUiSettingsForm
281 return _ApplicationUiSettingsForm
283
282
284
283
285 def DefaultPermissionsForm(repo_perms_choices, group_perms_choices, register_choices, create_choices,
284 def DefaultPermissionsForm(repo_perms_choices, group_perms_choices,
286 fork_choices):
285 register_choices, create_choices, fork_choices):
287 class _DefaultPermissionsForm(formencode.Schema):
286 class _DefaultPermissionsForm(formencode.Schema):
288 allow_extra_fields = True
287 allow_extra_fields = True
289 filter_extra_fields = True
288 filter_extra_fields = True
290 overwrite_default_repo = v.StringBoolean(if_missing=False)
289 overwrite_default_repo = v.StringBoolean(if_missing=False)
291 overwrite_default_group = v.StringBoolean(if_missing=False)
290 overwrite_default_group = v.StringBoolean(if_missing=False)
292 anonymous = v.StringBoolean(if_missing=False)
291 anonymous = v.StringBoolean(if_missing=False)
293 default_repo_perm = v.OneOf(repo_perms_choices)
292 default_repo_perm = v.OneOf(repo_perms_choices)
294 default_group_perm = v.OneOf(group_perms_choices)
293 default_group_perm = v.OneOf(group_perms_choices)
295 default_register = v.OneOf(register_choices)
294 default_register = v.OneOf(register_choices)
296 default_create = v.OneOf(create_choices)
295 default_create = v.OneOf(create_choices)
297 default_fork = v.OneOf(fork_choices)
296 default_fork = v.OneOf(fork_choices)
298
297
299 return _DefaultPermissionsForm
298 return _DefaultPermissionsForm
300
299
301
300
301 def DefaultsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
302 class _DefaultsForm(formencode.Schema):
303 allow_extra_fields = True
304 filter_extra_fields = True
305 default_repo_type = v.OneOf(supported_backends)
306 default_repo_private = v.StringBoolean(if_missing=False)
307 default_repo_enable_statistics = v.StringBoolean(if_missing=False)
308 default_repo_enable_downloads = v.StringBoolean(if_missing=False)
309 default_repo_enable_locking = v.StringBoolean(if_missing=False)
310
311 return _DefaultsForm
312
313
302 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices,
314 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices,
303 tls_kind_choices):
315 tls_kind_choices):
304 class _LdapSettingsForm(formencode.Schema):
316 class _LdapSettingsForm(formencode.Schema):
305 allow_extra_fields = True
317 allow_extra_fields = True
306 filter_extra_fields = True
318 filter_extra_fields = True
307 #pre_validators = [LdapLibValidator]
319 #pre_validators = [LdapLibValidator]
308 ldap_active = v.StringBoolean(if_missing=False)
320 ldap_active = v.StringBoolean(if_missing=False)
309 ldap_host = v.UnicodeString(strip=True,)
321 ldap_host = v.UnicodeString(strip=True,)
310 ldap_port = v.Number(strip=True,)
322 ldap_port = v.Number(strip=True,)
311 ldap_tls_kind = v.OneOf(tls_kind_choices)
323 ldap_tls_kind = v.OneOf(tls_kind_choices)
312 ldap_tls_reqcert = v.OneOf(tls_reqcert_choices)
324 ldap_tls_reqcert = v.OneOf(tls_reqcert_choices)
313 ldap_dn_user = v.UnicodeString(strip=True,)
325 ldap_dn_user = v.UnicodeString(strip=True,)
314 ldap_dn_pass = v.UnicodeString(strip=True,)
326 ldap_dn_pass = v.UnicodeString(strip=True,)
315 ldap_base_dn = v.UnicodeString(strip=True,)
327 ldap_base_dn = v.UnicodeString(strip=True,)
316 ldap_filter = v.UnicodeString(strip=True,)
328 ldap_filter = v.UnicodeString(strip=True,)
317 ldap_search_scope = v.OneOf(search_scope_choices)
329 ldap_search_scope = v.OneOf(search_scope_choices)
318 ldap_attr_login = All(
330 ldap_attr_login = All(
319 v.AttrLoginValidator(),
331 v.AttrLoginValidator(),
320 v.UnicodeString(strip=True,)
332 v.UnicodeString(strip=True,)
321 )
333 )
322 ldap_attr_firstname = v.UnicodeString(strip=True,)
334 ldap_attr_firstname = v.UnicodeString(strip=True,)
323 ldap_attr_lastname = v.UnicodeString(strip=True,)
335 ldap_attr_lastname = v.UnicodeString(strip=True,)
324 ldap_attr_email = v.UnicodeString(strip=True,)
336 ldap_attr_email = v.UnicodeString(strip=True,)
325
337
326 return _LdapSettingsForm
338 return _LdapSettingsForm
327
339
328
340
329 def UserExtraEmailForm():
341 def UserExtraEmailForm():
330 class _UserExtraEmailForm(formencode.Schema):
342 class _UserExtraEmailForm(formencode.Schema):
331 email = All(v.UniqSystemEmail(), v.Email)
343 email = All(v.UniqSystemEmail(), v.Email)
332
344
333 return _UserExtraEmailForm
345 return _UserExtraEmailForm
334
346
335
347
336 def PullRequestForm(repo_id):
348 def PullRequestForm(repo_id):
337 class _PullRequestForm(formencode.Schema):
349 class _PullRequestForm(formencode.Schema):
338 allow_extra_fields = True
350 allow_extra_fields = True
339 filter_extra_fields = True
351 filter_extra_fields = True
340
352
341 user = v.UnicodeString(strip=True, required=True)
353 user = v.UnicodeString(strip=True, required=True)
342 org_repo = v.UnicodeString(strip=True, required=True)
354 org_repo = v.UnicodeString(strip=True, required=True)
343 org_ref = v.UnicodeString(strip=True, required=True)
355 org_ref = v.UnicodeString(strip=True, required=True)
344 other_repo = v.UnicodeString(strip=True, required=True)
356 other_repo = v.UnicodeString(strip=True, required=True)
345 other_ref = v.UnicodeString(strip=True, required=True)
357 other_ref = v.UnicodeString(strip=True, required=True)
346 revisions = All(v.NotReviewedRevisions(repo_id)(), v.UniqueList(not_empty=True))
358 revisions = All(v.NotReviewedRevisions(repo_id)(), v.UniqueList(not_empty=True))
347 review_members = v.UniqueList(not_empty=True)
359 review_members = v.UniqueList(not_empty=True)
348
360
349 pullrequest_title = v.UnicodeString(strip=True, required=True, min=3)
361 pullrequest_title = v.UnicodeString(strip=True, required=True, min=3)
350 pullrequest_desc = v.UnicodeString(strip=True, required=False)
362 pullrequest_desc = v.UnicodeString(strip=True, required=False)
351
363
352 return _PullRequestForm
364 return _PullRequestForm
@@ -1,551 +1,564 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.repo
3 rhodecode.model.repo
4 ~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~
5
5
6 Repository model for rhodecode
6 Repository model for rhodecode
7
7
8 :created_on: Jun 5, 2010
8 :created_on: Jun 5, 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 from __future__ import with_statement
25 from __future__ import with_statement
26 import os
26 import os
27 import shutil
27 import shutil
28 import logging
28 import logging
29 import traceback
29 import traceback
30 from datetime import datetime
30 from datetime import datetime
31
31
32 from rhodecode.lib.vcs.backends import get_backend
32 from rhodecode.lib.vcs.backends import get_backend
33 from rhodecode.lib.compat import json
33 from rhodecode.lib.compat import json
34 from rhodecode.lib.utils2 import LazyProperty, safe_str, safe_unicode
34 from rhodecode.lib.utils2 import LazyProperty, safe_str, safe_unicode
35 from rhodecode.lib.caching_query import FromCache
35 from rhodecode.lib.caching_query import FromCache
36 from rhodecode.lib.hooks import log_create_repository, log_delete_repository
36 from rhodecode.lib.hooks import log_create_repository, log_delete_repository
37
37
38 from rhodecode.model import BaseModel
38 from rhodecode.model import BaseModel
39 from rhodecode.model.db import Repository, UserRepoToPerm, User, Permission, \
39 from rhodecode.model.db import Repository, UserRepoToPerm, User, Permission, \
40 Statistics, UsersGroup, UsersGroupRepoToPerm, RhodeCodeUi, RepoGroup
40 Statistics, UsersGroup, UsersGroupRepoToPerm, RhodeCodeUi, RepoGroup,\
41 RhodeCodeSetting
41 from rhodecode.lib import helpers as h
42 from rhodecode.lib import helpers as h
42
43
43
44
44 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
45
46
46
47
47 class RepoModel(BaseModel):
48 class RepoModel(BaseModel):
48
49
49 cls = Repository
50 cls = Repository
50 URL_SEPARATOR = Repository.url_sep()
51 URL_SEPARATOR = Repository.url_sep()
51
52
52 def __get_users_group(self, users_group):
53 def __get_users_group(self, users_group):
53 return self._get_instance(UsersGroup, users_group,
54 return self._get_instance(UsersGroup, users_group,
54 callback=UsersGroup.get_by_group_name)
55 callback=UsersGroup.get_by_group_name)
55
56
56 def _get_repos_group(self, repos_group):
57 def _get_repos_group(self, repos_group):
57 return self._get_instance(RepoGroup, repos_group,
58 return self._get_instance(RepoGroup, repos_group,
58 callback=RepoGroup.get_by_group_name)
59 callback=RepoGroup.get_by_group_name)
59
60
60 @LazyProperty
61 @LazyProperty
61 def repos_path(self):
62 def repos_path(self):
62 """
63 """
63 Get's the repositories root path from database
64 Get's the repositories root path from database
64 """
65 """
65
66
66 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
67 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
67 return q.ui_value
68 return q.ui_value
68
69
69 def get(self, repo_id, cache=False):
70 def get(self, repo_id, cache=False):
70 repo = self.sa.query(Repository)\
71 repo = self.sa.query(Repository)\
71 .filter(Repository.repo_id == repo_id)
72 .filter(Repository.repo_id == repo_id)
72
73
73 if cache:
74 if cache:
74 repo = repo.options(FromCache("sql_cache_short",
75 repo = repo.options(FromCache("sql_cache_short",
75 "get_repo_%s" % repo_id))
76 "get_repo_%s" % repo_id))
76 return repo.scalar()
77 return repo.scalar()
77
78
78 def get_repo(self, repository):
79 def get_repo(self, repository):
79 return self._get_repo(repository)
80 return self._get_repo(repository)
80
81
81 def get_by_repo_name(self, repo_name, cache=False):
82 def get_by_repo_name(self, repo_name, cache=False):
82 repo = self.sa.query(Repository)\
83 repo = self.sa.query(Repository)\
83 .filter(Repository.repo_name == repo_name)
84 .filter(Repository.repo_name == repo_name)
84
85
85 if cache:
86 if cache:
86 repo = repo.options(FromCache("sql_cache_short",
87 repo = repo.options(FromCache("sql_cache_short",
87 "get_repo_%s" % repo_name))
88 "get_repo_%s" % repo_name))
88 return repo.scalar()
89 return repo.scalar()
89
90
90 def get_users_js(self):
91 def get_users_js(self):
91 users = self.sa.query(User).filter(User.active == True).all()
92 users = self.sa.query(User).filter(User.active == True).all()
92 return json.dumps([
93 return json.dumps([
93 {
94 {
94 'id': u.user_id,
95 'id': u.user_id,
95 'fname': u.name,
96 'fname': u.name,
96 'lname': u.lastname,
97 'lname': u.lastname,
97 'nname': u.username,
98 'nname': u.username,
98 'gravatar_lnk': h.gravatar_url(u.email, 14)
99 'gravatar_lnk': h.gravatar_url(u.email, 14)
99 } for u in users]
100 } for u in users]
100 )
101 )
101
102
102 def get_users_groups_js(self):
103 def get_users_groups_js(self):
103 users_groups = self.sa.query(UsersGroup)\
104 users_groups = self.sa.query(UsersGroup)\
104 .filter(UsersGroup.users_group_active == True).all()
105 .filter(UsersGroup.users_group_active == True).all()
105
106
106 return json.dumps([
107 return json.dumps([
107 {
108 {
108 'id': gr.users_group_id,
109 'id': gr.users_group_id,
109 'grname': gr.users_group_name,
110 'grname': gr.users_group_name,
110 'grmembers': len(gr.members),
111 'grmembers': len(gr.members),
111 } for gr in users_groups]
112 } for gr in users_groups]
112 )
113 )
113
114
114 def _get_defaults(self, repo_name):
115 def _get_defaults(self, repo_name):
115 """
116 """
116 Get's information about repository, and returns a dict for
117 Get's information about repository, and returns a dict for
117 usage in forms
118 usage in forms
118
119
119 :param repo_name:
120 :param repo_name:
120 """
121 """
121
122
122 repo_info = Repository.get_by_repo_name(repo_name)
123 repo_info = Repository.get_by_repo_name(repo_name)
123
124
124 if repo_info is None:
125 if repo_info is None:
125 return None
126 return None
126
127
127 defaults = repo_info.get_dict()
128 defaults = repo_info.get_dict()
128 group, repo_name = repo_info.groups_and_repo
129 group, repo_name = repo_info.groups_and_repo
129 defaults['repo_name'] = repo_name
130 defaults['repo_name'] = repo_name
130 defaults['repo_group'] = getattr(group[-1] if group else None,
131 defaults['repo_group'] = getattr(group[-1] if group else None,
131 'group_id', None)
132 'group_id', None)
132
133
133 # fill owner
134 # fill owner
134 if repo_info.user:
135 if repo_info.user:
135 defaults.update({'user': repo_info.user.username})
136 defaults.update({'user': repo_info.user.username})
136 else:
137 else:
137 replacement_user = User.query().filter(User.admin ==
138 replacement_user = User.query().filter(User.admin ==
138 True).first().username
139 True).first().username
139 defaults.update({'user': replacement_user})
140 defaults.update({'user': replacement_user})
140
141
141 # fill repository users
142 # fill repository users
142 for p in repo_info.repo_to_perm:
143 for p in repo_info.repo_to_perm:
143 defaults.update({'u_perm_%s' % p.user.username:
144 defaults.update({'u_perm_%s' % p.user.username:
144 p.permission.permission_name})
145 p.permission.permission_name})
145
146
146 # fill repository groups
147 # fill repository groups
147 for p in repo_info.users_group_to_perm:
148 for p in repo_info.users_group_to_perm:
148 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
149 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
149 p.permission.permission_name})
150 p.permission.permission_name})
150
151
151 return defaults
152 return defaults
152
153
153 def update(self, repo_name, form_data):
154 def update(self, repo_name, form_data):
154 try:
155 try:
155 cur_repo = self.get_by_repo_name(repo_name, cache=False)
156 cur_repo = self.get_by_repo_name(repo_name, cache=False)
156
157
157 # update permissions
158 # update permissions
158 for member, perm, member_type in form_data['perms_updates']:
159 for member, perm, member_type in form_data['perms_updates']:
159 if member_type == 'user':
160 if member_type == 'user':
160 # this updates existing one
161 # this updates existing one
161 RepoModel().grant_user_permission(
162 RepoModel().grant_user_permission(
162 repo=cur_repo, user=member, perm=perm
163 repo=cur_repo, user=member, perm=perm
163 )
164 )
164 else:
165 else:
165 RepoModel().grant_users_group_permission(
166 RepoModel().grant_users_group_permission(
166 repo=cur_repo, group_name=member, perm=perm
167 repo=cur_repo, group_name=member, perm=perm
167 )
168 )
168 # set new permissions
169 # set new permissions
169 for member, perm, member_type in form_data['perms_new']:
170 for member, perm, member_type in form_data['perms_new']:
170 if member_type == 'user':
171 if member_type == 'user':
171 RepoModel().grant_user_permission(
172 RepoModel().grant_user_permission(
172 repo=cur_repo, user=member, perm=perm
173 repo=cur_repo, user=member, perm=perm
173 )
174 )
174 else:
175 else:
175 RepoModel().grant_users_group_permission(
176 RepoModel().grant_users_group_permission(
176 repo=cur_repo, group_name=member, perm=perm
177 repo=cur_repo, group_name=member, perm=perm
177 )
178 )
178
179
179 # update current repo
180 # update current repo
180 for k, v in form_data.items():
181 for k, v in form_data.items():
181 if k == 'user':
182 if k == 'user':
182 cur_repo.user = User.get_by_username(v)
183 cur_repo.user = User.get_by_username(v)
183 elif k == 'repo_name':
184 elif k == 'repo_name':
184 pass
185 pass
185 elif k == 'repo_group':
186 elif k == 'repo_group':
186 cur_repo.group = RepoGroup.get(v)
187 cur_repo.group = RepoGroup.get(v)
187
188
188 else:
189 else:
189 setattr(cur_repo, k, v)
190 setattr(cur_repo, k, v)
190
191
191 new_name = cur_repo.get_new_name(form_data['repo_name'])
192 new_name = cur_repo.get_new_name(form_data['repo_name'])
192 cur_repo.repo_name = new_name
193 cur_repo.repo_name = new_name
193
194
194 self.sa.add(cur_repo)
195 self.sa.add(cur_repo)
195
196
196 if repo_name != new_name:
197 if repo_name != new_name:
197 # rename repository
198 # rename repository
198 self.__rename_repo(old=repo_name, new=new_name)
199 self.__rename_repo(old=repo_name, new=new_name)
199
200
200 return cur_repo
201 return cur_repo
201 except:
202 except:
202 log.error(traceback.format_exc())
203 log.error(traceback.format_exc())
203 raise
204 raise
204
205
205 def create_repo(self, repo_name, repo_type, description, owner,
206 def create_repo(self, repo_name, repo_type, description, owner,
206 private=False, clone_uri=None, repos_group=None,
207 private=False, clone_uri=None, repos_group=None,
207 landing_rev='tip', just_db=False, fork_of=None,
208 landing_rev='tip', just_db=False, fork_of=None,
208 copy_fork_permissions=False):
209 copy_fork_permissions=False, enable_statistics=False,
210 enable_locking=False, enable_downloads=False):
209 """
211 """
210 Create repository
212 Create repository
211
213
212 """
214 """
213 from rhodecode.model.scm import ScmModel
215 from rhodecode.model.scm import ScmModel
214
216
215 owner = self._get_user(owner)
217 owner = self._get_user(owner)
216 fork_of = self._get_repo(fork_of)
218 fork_of = self._get_repo(fork_of)
217 repos_group = self._get_repos_group(repos_group)
219 repos_group = self._get_repos_group(repos_group)
218 try:
220 try:
219
221
220 # repo name is just a name of repository
222 # repo name is just a name of repository
221 # while repo_name_full is a full qualified name that is combined
223 # while repo_name_full is a full qualified name that is combined
222 # with name and path of group
224 # with name and path of group
223 repo_name_full = repo_name
225 repo_name_full = repo_name
224 repo_name = repo_name.split(self.URL_SEPARATOR)[-1]
226 repo_name = repo_name.split(self.URL_SEPARATOR)[-1]
225
227
226 new_repo = Repository()
228 new_repo = Repository()
227 new_repo.enable_statistics = False
229 new_repo.enable_statistics = False
228 new_repo.repo_name = repo_name_full
230 new_repo.repo_name = repo_name_full
229 new_repo.repo_type = repo_type
231 new_repo.repo_type = repo_type
230 new_repo.user = owner
232 new_repo.user = owner
231 new_repo.group = repos_group
233 new_repo.group = repos_group
232 new_repo.description = description or repo_name
234 new_repo.description = description or repo_name
233 new_repo.private = private
235 new_repo.private = private
234 new_repo.clone_uri = clone_uri
236 new_repo.clone_uri = clone_uri
235 new_repo.landing_rev = landing_rev
237 new_repo.landing_rev = landing_rev
236
238
239 new_repo.enable_statistics = enable_statistics
240 new_repo.enable_locking = enable_locking
241 new_repo.enable_downloads = enable_downloads
242
237 if repos_group:
243 if repos_group:
238 new_repo.enable_locking = repos_group.enable_locking
244 new_repo.enable_locking = repos_group.enable_locking
239
245
240 if fork_of:
246 if fork_of:
241 parent_repo = fork_of
247 parent_repo = fork_of
242 new_repo.fork = parent_repo
248 new_repo.fork = parent_repo
243
249
244 self.sa.add(new_repo)
250 self.sa.add(new_repo)
245
251
246 def _create_default_perms():
252 def _create_default_perms():
247 # create default permission
253 # create default permission
248 repo_to_perm = UserRepoToPerm()
254 repo_to_perm = UserRepoToPerm()
249 default = 'repository.read'
255 default = 'repository.read'
250 for p in User.get_by_username('default').user_perms:
256 for p in User.get_by_username('default').user_perms:
251 if p.permission.permission_name.startswith('repository.'):
257 if p.permission.permission_name.startswith('repository.'):
252 default = p.permission.permission_name
258 default = p.permission.permission_name
253 break
259 break
254
260
255 default_perm = 'repository.none' if private else default
261 default_perm = 'repository.none' if private else default
256
262
257 repo_to_perm.permission_id = self.sa.query(Permission)\
263 repo_to_perm.permission_id = self.sa.query(Permission)\
258 .filter(Permission.permission_name == default_perm)\
264 .filter(Permission.permission_name == default_perm)\
259 .one().permission_id
265 .one().permission_id
260
266
261 repo_to_perm.repository = new_repo
267 repo_to_perm.repository = new_repo
262 repo_to_perm.user_id = User.get_by_username('default').user_id
268 repo_to_perm.user_id = User.get_by_username('default').user_id
263
269
264 self.sa.add(repo_to_perm)
270 self.sa.add(repo_to_perm)
265
271
266 if fork_of:
272 if fork_of:
267 if copy_fork_permissions:
273 if copy_fork_permissions:
268 repo = fork_of
274 repo = fork_of
269 user_perms = UserRepoToPerm.query()\
275 user_perms = UserRepoToPerm.query()\
270 .filter(UserRepoToPerm.repository == repo).all()
276 .filter(UserRepoToPerm.repository == repo).all()
271 group_perms = UsersGroupRepoToPerm.query()\
277 group_perms = UsersGroupRepoToPerm.query()\
272 .filter(UsersGroupRepoToPerm.repository == repo).all()
278 .filter(UsersGroupRepoToPerm.repository == repo).all()
273
279
274 for perm in user_perms:
280 for perm in user_perms:
275 UserRepoToPerm.create(perm.user, new_repo,
281 UserRepoToPerm.create(perm.user, new_repo,
276 perm.permission)
282 perm.permission)
277
283
278 for perm in group_perms:
284 for perm in group_perms:
279 UsersGroupRepoToPerm.create(perm.users_group, new_repo,
285 UsersGroupRepoToPerm.create(perm.users_group, new_repo,
280 perm.permission)
286 perm.permission)
281 else:
287 else:
282 _create_default_perms()
288 _create_default_perms()
283 else:
289 else:
284 _create_default_perms()
290 _create_default_perms()
285
291
286 if not just_db:
292 if not just_db:
287 self.__create_repo(repo_name, repo_type,
293 self.__create_repo(repo_name, repo_type,
288 repos_group,
294 repos_group,
289 clone_uri)
295 clone_uri)
290 log_create_repository(new_repo.get_dict(),
296 log_create_repository(new_repo.get_dict(),
291 created_by=owner.username)
297 created_by=owner.username)
292
298
293 # now automatically start following this repository as owner
299 # now automatically start following this repository as owner
294 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
300 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
295 owner.user_id)
301 owner.user_id)
296 return new_repo
302 return new_repo
297 except:
303 except:
298 log.error(traceback.format_exc())
304 log.error(traceback.format_exc())
299 raise
305 raise
300
306
301 def create(self, form_data, cur_user, just_db=False, fork=None):
307 def create(self, form_data, cur_user, just_db=False, fork=None):
302 """
308 """
303 Backward compatibility function, just a wrapper on top of create_repo
309 Backward compatibility function, just a wrapper on top of create_repo
304
310
305 :param form_data:
311 :param form_data:
306 :param cur_user:
312 :param cur_user:
307 :param just_db:
313 :param just_db:
308 :param fork:
314 :param fork:
309 """
315 """
310
316 owner = cur_user
311 repo_name = form_data['repo_name_full']
317 repo_name = form_data['repo_name_full']
312 repo_type = form_data['repo_type']
318 repo_type = form_data['repo_type']
313 description = form_data['description']
319 description = form_data['repo_description']
314 owner = cur_user
320 private = form_data['repo_private']
315 private = form_data['private']
316 clone_uri = form_data.get('clone_uri')
321 clone_uri = form_data.get('clone_uri')
317 repos_group = form_data['repo_group']
322 repos_group = form_data['repo_group']
318 landing_rev = form_data['landing_rev']
323 landing_rev = form_data['repo_landing_rev']
319 copy_fork_permissions = form_data.get('copy_permissions')
324 copy_fork_permissions = form_data.get('copy_permissions')
320 fork_of = form_data.get('fork_parent_id')
325 fork_of = form_data.get('fork_parent_id')
326
327 ##defaults
328 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
329 enable_statistics = defs.get('repo_enable_statistic')
330 enable_locking = defs.get('repo_enable_locking')
331 enable_downloads = defs.get('repo_enable_downloads')
332
321 return self.create_repo(
333 return self.create_repo(
322 repo_name, repo_type, description, owner, private, clone_uri,
334 repo_name, repo_type, description, owner, private, clone_uri,
323 repos_group, landing_rev, just_db, fork_of, copy_fork_permissions
335 repos_group, landing_rev, just_db, fork_of, copy_fork_permissions,
336 enable_statistics, enable_locking, enable_downloads
324 )
337 )
325
338
326 def create_fork(self, form_data, cur_user):
339 def create_fork(self, form_data, cur_user):
327 """
340 """
328 Simple wrapper into executing celery task for fork creation
341 Simple wrapper into executing celery task for fork creation
329
342
330 :param form_data:
343 :param form_data:
331 :param cur_user:
344 :param cur_user:
332 """
345 """
333 from rhodecode.lib.celerylib import tasks, run_task
346 from rhodecode.lib.celerylib import tasks, run_task
334 run_task(tasks.create_repo_fork, form_data, cur_user)
347 run_task(tasks.create_repo_fork, form_data, cur_user)
335
348
336 def delete(self, repo):
349 def delete(self, repo):
337 repo = self._get_repo(repo)
350 repo = self._get_repo(repo)
338 if repo:
351 if repo:
339 old_repo_dict = repo.get_dict()
352 old_repo_dict = repo.get_dict()
340 owner = repo.user
353 owner = repo.user
341 try:
354 try:
342 self.sa.delete(repo)
355 self.sa.delete(repo)
343 self.__delete_repo(repo)
356 self.__delete_repo(repo)
344 log_delete_repository(old_repo_dict,
357 log_delete_repository(old_repo_dict,
345 deleted_by=owner.username)
358 deleted_by=owner.username)
346 except:
359 except:
347 log.error(traceback.format_exc())
360 log.error(traceback.format_exc())
348 raise
361 raise
349
362
350 def grant_user_permission(self, repo, user, perm):
363 def grant_user_permission(self, repo, user, perm):
351 """
364 """
352 Grant permission for user on given repository, or update existing one
365 Grant permission for user on given repository, or update existing one
353 if found
366 if found
354
367
355 :param repo: Instance of Repository, repository_id, or repository name
368 :param repo: Instance of Repository, repository_id, or repository name
356 :param user: Instance of User, user_id or username
369 :param user: Instance of User, user_id or username
357 :param perm: Instance of Permission, or permission_name
370 :param perm: Instance of Permission, or permission_name
358 """
371 """
359 user = self._get_user(user)
372 user = self._get_user(user)
360 repo = self._get_repo(repo)
373 repo = self._get_repo(repo)
361 permission = self._get_perm(perm)
374 permission = self._get_perm(perm)
362
375
363 # check if we have that permission already
376 # check if we have that permission already
364 obj = self.sa.query(UserRepoToPerm)\
377 obj = self.sa.query(UserRepoToPerm)\
365 .filter(UserRepoToPerm.user == user)\
378 .filter(UserRepoToPerm.user == user)\
366 .filter(UserRepoToPerm.repository == repo)\
379 .filter(UserRepoToPerm.repository == repo)\
367 .scalar()
380 .scalar()
368 if obj is None:
381 if obj is None:
369 # create new !
382 # create new !
370 obj = UserRepoToPerm()
383 obj = UserRepoToPerm()
371 obj.repository = repo
384 obj.repository = repo
372 obj.user = user
385 obj.user = user
373 obj.permission = permission
386 obj.permission = permission
374 self.sa.add(obj)
387 self.sa.add(obj)
375 log.debug('Granted perm %s to %s on %s' % (perm, user, repo))
388 log.debug('Granted perm %s to %s on %s' % (perm, user, repo))
376
389
377 def revoke_user_permission(self, repo, user):
390 def revoke_user_permission(self, repo, user):
378 """
391 """
379 Revoke permission for user on given repository
392 Revoke permission for user on given repository
380
393
381 :param repo: Instance of Repository, repository_id, or repository name
394 :param repo: Instance of Repository, repository_id, or repository name
382 :param user: Instance of User, user_id or username
395 :param user: Instance of User, user_id or username
383 """
396 """
384
397
385 user = self._get_user(user)
398 user = self._get_user(user)
386 repo = self._get_repo(repo)
399 repo = self._get_repo(repo)
387
400
388 obj = self.sa.query(UserRepoToPerm)\
401 obj = self.sa.query(UserRepoToPerm)\
389 .filter(UserRepoToPerm.repository == repo)\
402 .filter(UserRepoToPerm.repository == repo)\
390 .filter(UserRepoToPerm.user == user)\
403 .filter(UserRepoToPerm.user == user)\
391 .scalar()
404 .scalar()
392 if obj:
405 if obj:
393 self.sa.delete(obj)
406 self.sa.delete(obj)
394 log.debug('Revoked perm on %s on %s' % (repo, user))
407 log.debug('Revoked perm on %s on %s' % (repo, user))
395
408
396 def grant_users_group_permission(self, repo, group_name, perm):
409 def grant_users_group_permission(self, repo, group_name, perm):
397 """
410 """
398 Grant permission for users group on given repository, or update
411 Grant permission for users group on given repository, or update
399 existing one if found
412 existing one if found
400
413
401 :param repo: Instance of Repository, repository_id, or repository name
414 :param repo: Instance of Repository, repository_id, or repository name
402 :param group_name: Instance of UserGroup, users_group_id,
415 :param group_name: Instance of UserGroup, users_group_id,
403 or users group name
416 or users group name
404 :param perm: Instance of Permission, or permission_name
417 :param perm: Instance of Permission, or permission_name
405 """
418 """
406 repo = self._get_repo(repo)
419 repo = self._get_repo(repo)
407 group_name = self.__get_users_group(group_name)
420 group_name = self.__get_users_group(group_name)
408 permission = self._get_perm(perm)
421 permission = self._get_perm(perm)
409
422
410 # check if we have that permission already
423 # check if we have that permission already
411 obj = self.sa.query(UsersGroupRepoToPerm)\
424 obj = self.sa.query(UsersGroupRepoToPerm)\
412 .filter(UsersGroupRepoToPerm.users_group == group_name)\
425 .filter(UsersGroupRepoToPerm.users_group == group_name)\
413 .filter(UsersGroupRepoToPerm.repository == repo)\
426 .filter(UsersGroupRepoToPerm.repository == repo)\
414 .scalar()
427 .scalar()
415
428
416 if obj is None:
429 if obj is None:
417 # create new
430 # create new
418 obj = UsersGroupRepoToPerm()
431 obj = UsersGroupRepoToPerm()
419
432
420 obj.repository = repo
433 obj.repository = repo
421 obj.users_group = group_name
434 obj.users_group = group_name
422 obj.permission = permission
435 obj.permission = permission
423 self.sa.add(obj)
436 self.sa.add(obj)
424 log.debug('Granted perm %s to %s on %s' % (perm, group_name, repo))
437 log.debug('Granted perm %s to %s on %s' % (perm, group_name, repo))
425
438
426 def revoke_users_group_permission(self, repo, group_name):
439 def revoke_users_group_permission(self, repo, group_name):
427 """
440 """
428 Revoke permission for users group on given repository
441 Revoke permission for users group on given repository
429
442
430 :param repo: Instance of Repository, repository_id, or repository name
443 :param repo: Instance of Repository, repository_id, or repository name
431 :param group_name: Instance of UserGroup, users_group_id,
444 :param group_name: Instance of UserGroup, users_group_id,
432 or users group name
445 or users group name
433 """
446 """
434 repo = self._get_repo(repo)
447 repo = self._get_repo(repo)
435 group_name = self.__get_users_group(group_name)
448 group_name = self.__get_users_group(group_name)
436
449
437 obj = self.sa.query(UsersGroupRepoToPerm)\
450 obj = self.sa.query(UsersGroupRepoToPerm)\
438 .filter(UsersGroupRepoToPerm.repository == repo)\
451 .filter(UsersGroupRepoToPerm.repository == repo)\
439 .filter(UsersGroupRepoToPerm.users_group == group_name)\
452 .filter(UsersGroupRepoToPerm.users_group == group_name)\
440 .scalar()
453 .scalar()
441 if obj:
454 if obj:
442 self.sa.delete(obj)
455 self.sa.delete(obj)
443 log.debug('Revoked perm to %s on %s' % (repo, group_name))
456 log.debug('Revoked perm to %s on %s' % (repo, group_name))
444
457
445 def delete_stats(self, repo_name):
458 def delete_stats(self, repo_name):
446 """
459 """
447 removes stats for given repo
460 removes stats for given repo
448
461
449 :param repo_name:
462 :param repo_name:
450 """
463 """
451 try:
464 try:
452 obj = self.sa.query(Statistics)\
465 obj = self.sa.query(Statistics)\
453 .filter(Statistics.repository ==
466 .filter(Statistics.repository ==
454 self.get_by_repo_name(repo_name))\
467 self.get_by_repo_name(repo_name))\
455 .one()
468 .one()
456 self.sa.delete(obj)
469 self.sa.delete(obj)
457 except:
470 except:
458 log.error(traceback.format_exc())
471 log.error(traceback.format_exc())
459 raise
472 raise
460
473
461 def __create_repo(self, repo_name, alias, parent, clone_uri=False):
474 def __create_repo(self, repo_name, alias, parent, clone_uri=False):
462 """
475 """
463 makes repository on filesystem. It's group aware means it'll create
476 makes repository on filesystem. It's group aware means it'll create
464 a repository within a group, and alter the paths accordingly of
477 a repository within a group, and alter the paths accordingly of
465 group location
478 group location
466
479
467 :param repo_name:
480 :param repo_name:
468 :param alias:
481 :param alias:
469 :param parent_id:
482 :param parent_id:
470 :param clone_uri:
483 :param clone_uri:
471 """
484 """
472 from rhodecode.lib.utils import is_valid_repo, is_valid_repos_group
485 from rhodecode.lib.utils import is_valid_repo, is_valid_repos_group
473 from rhodecode.model.scm import ScmModel
486 from rhodecode.model.scm import ScmModel
474
487
475 if parent:
488 if parent:
476 new_parent_path = os.sep.join(parent.full_path_splitted)
489 new_parent_path = os.sep.join(parent.full_path_splitted)
477 else:
490 else:
478 new_parent_path = ''
491 new_parent_path = ''
479
492
480 # we need to make it str for mercurial
493 # we need to make it str for mercurial
481 repo_path = os.path.join(*map(lambda x: safe_str(x),
494 repo_path = os.path.join(*map(lambda x: safe_str(x),
482 [self.repos_path, new_parent_path, repo_name]))
495 [self.repos_path, new_parent_path, repo_name]))
483
496
484 # check if this path is not a repository
497 # check if this path is not a repository
485 if is_valid_repo(repo_path, self.repos_path):
498 if is_valid_repo(repo_path, self.repos_path):
486 raise Exception('This path %s is a valid repository' % repo_path)
499 raise Exception('This path %s is a valid repository' % repo_path)
487
500
488 # check if this path is a group
501 # check if this path is a group
489 if is_valid_repos_group(repo_path, self.repos_path):
502 if is_valid_repos_group(repo_path, self.repos_path):
490 raise Exception('This path %s is a valid group' % repo_path)
503 raise Exception('This path %s is a valid group' % repo_path)
491
504
492 log.info('creating repo %s in %s @ %s' % (
505 log.info('creating repo %s in %s @ %s' % (
493 repo_name, safe_unicode(repo_path), clone_uri
506 repo_name, safe_unicode(repo_path), clone_uri
494 )
507 )
495 )
508 )
496 backend = get_backend(alias)
509 backend = get_backend(alias)
497 if alias == 'hg':
510 if alias == 'hg':
498 backend(repo_path, create=True, src_url=clone_uri)
511 backend(repo_path, create=True, src_url=clone_uri)
499 elif alias == 'git':
512 elif alias == 'git':
500 r = backend(repo_path, create=True, src_url=clone_uri, bare=True)
513 r = backend(repo_path, create=True, src_url=clone_uri, bare=True)
501 # add rhodecode hook into this repo
514 # add rhodecode hook into this repo
502 ScmModel().install_git_hook(repo=r)
515 ScmModel().install_git_hook(repo=r)
503 else:
516 else:
504 raise Exception('Undefined alias %s' % alias)
517 raise Exception('Undefined alias %s' % alias)
505
518
506 def __rename_repo(self, old, new):
519 def __rename_repo(self, old, new):
507 """
520 """
508 renames repository on filesystem
521 renames repository on filesystem
509
522
510 :param old: old name
523 :param old: old name
511 :param new: new name
524 :param new: new name
512 """
525 """
513 log.info('renaming repo from %s to %s' % (old, new))
526 log.info('renaming repo from %s to %s' % (old, new))
514
527
515 old_path = os.path.join(self.repos_path, old)
528 old_path = os.path.join(self.repos_path, old)
516 new_path = os.path.join(self.repos_path, new)
529 new_path = os.path.join(self.repos_path, new)
517 if os.path.isdir(new_path):
530 if os.path.isdir(new_path):
518 raise Exception(
531 raise Exception(
519 'Was trying to rename to already existing dir %s' % new_path
532 'Was trying to rename to already existing dir %s' % new_path
520 )
533 )
521 shutil.move(old_path, new_path)
534 shutil.move(old_path, new_path)
522
535
523 def __delete_repo(self, repo):
536 def __delete_repo(self, repo):
524 """
537 """
525 removes repo from filesystem, the removal is acctually made by
538 removes repo from filesystem, the removal is acctually made by
526 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
539 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
527 repository is no longer valid for rhodecode, can be undeleted later on
540 repository is no longer valid for rhodecode, can be undeleted later on
528 by reverting the renames on this repository
541 by reverting the renames on this repository
529
542
530 :param repo: repo object
543 :param repo: repo object
531 """
544 """
532 rm_path = os.path.join(self.repos_path, repo.repo_name)
545 rm_path = os.path.join(self.repos_path, repo.repo_name)
533 log.info("Removing %s" % (rm_path))
546 log.info("Removing %s" % (rm_path))
534 # disable hg/git internal that it doesn't get detected as repo
547 # disable hg/git internal that it doesn't get detected as repo
535 alias = repo.repo_type
548 alias = repo.repo_type
536
549
537 bare = getattr(repo.scm_instance, 'bare', False)
550 bare = getattr(repo.scm_instance, 'bare', False)
538
551
539 if not bare:
552 if not bare:
540 # skip this for bare git repos
553 # skip this for bare git repos
541 shutil.move(os.path.join(rm_path, '.%s' % alias),
554 shutil.move(os.path.join(rm_path, '.%s' % alias),
542 os.path.join(rm_path, 'rm__.%s' % alias))
555 os.path.join(rm_path, 'rm__.%s' % alias))
543 # disable repo
556 # disable repo
544 _now = datetime.now()
557 _now = datetime.now()
545 _ms = str(_now.microsecond).rjust(6, '0')
558 _ms = str(_now.microsecond).rjust(6, '0')
546 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
559 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
547 repo.just_name)
560 repo.just_name)
548 if repo.group:
561 if repo.group:
549 args = repo.group.full_path_splitted + [_d]
562 args = repo.group.full_path_splitted + [_d]
550 _d = os.path.join(*args)
563 _d = os.path.join(*args)
551 shutil.move(rm_path, os.path.join(self.repos_path, _d))
564 shutil.move(rm_path, os.path.join(self.repos_path, _d))
@@ -1,4759 +1,4767 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 div.h1,div.h2,div.h3,div.h4,div.h5,div.h6 {
65 div.h1,div.h2,div.h3,div.h4,div.h5,div.h6 {
66 color: #292929;
66 color: #292929;
67 font-weight: 700;
67 font-weight: 700;
68 }
68 }
69
69
70 h1,div.h1 {
70 h1,div.h1 {
71 font-size: 22px;
71 font-size: 22px;
72 }
72 }
73
73
74 h2,div.h2 {
74 h2,div.h2 {
75 font-size: 20px;
75 font-size: 20px;
76 }
76 }
77
77
78 h3,div.h3 {
78 h3,div.h3 {
79 font-size: 18px;
79 font-size: 18px;
80 }
80 }
81
81
82 h4,div.h4 {
82 h4,div.h4 {
83 font-size: 16px;
83 font-size: 16px;
84 }
84 }
85
85
86 h5,div.h5 {
86 h5,div.h5 {
87 font-size: 14px;
87 font-size: 14px;
88 }
88 }
89
89
90 h6,div.h6 {
90 h6,div.h6 {
91 font-size: 11px;
91 font-size: 11px;
92 }
92 }
93
93
94 ul.circle {
94 ul.circle {
95 list-style-type: circle;
95 list-style-type: circle;
96 }
96 }
97
97
98 ul.disc {
98 ul.disc {
99 list-style-type: disc;
99 list-style-type: disc;
100 }
100 }
101
101
102 ul.square {
102 ul.square {
103 list-style-type: square;
103 list-style-type: square;
104 }
104 }
105
105
106 ol.lower-roman {
106 ol.lower-roman {
107 list-style-type: lower-roman;
107 list-style-type: lower-roman;
108 }
108 }
109
109
110 ol.upper-roman {
110 ol.upper-roman {
111 list-style-type: upper-roman;
111 list-style-type: upper-roman;
112 }
112 }
113
113
114 ol.lower-alpha {
114 ol.lower-alpha {
115 list-style-type: lower-alpha;
115 list-style-type: lower-alpha;
116 }
116 }
117
117
118 ol.upper-alpha {
118 ol.upper-alpha {
119 list-style-type: upper-alpha;
119 list-style-type: upper-alpha;
120 }
120 }
121
121
122 ol.decimal {
122 ol.decimal {
123 list-style-type: decimal;
123 list-style-type: decimal;
124 }
124 }
125
125
126 div.color {
126 div.color {
127 clear: both;
127 clear: both;
128 overflow: hidden;
128 overflow: hidden;
129 position: absolute;
129 position: absolute;
130 background: #FFF;
130 background: #FFF;
131 margin: 7px 0 0 60px;
131 margin: 7px 0 0 60px;
132 padding: 1px 1px 1px 0;
132 padding: 1px 1px 1px 0;
133 }
133 }
134
134
135 div.color a {
135 div.color a {
136 width: 15px;
136 width: 15px;
137 height: 15px;
137 height: 15px;
138 display: block;
138 display: block;
139 float: left;
139 float: left;
140 margin: 0 0 0 1px;
140 margin: 0 0 0 1px;
141 padding: 0;
141 padding: 0;
142 }
142 }
143
143
144 div.options {
144 div.options {
145 clear: both;
145 clear: both;
146 overflow: hidden;
146 overflow: hidden;
147 position: absolute;
147 position: absolute;
148 background: #FFF;
148 background: #FFF;
149 margin: 7px 0 0 162px;
149 margin: 7px 0 0 162px;
150 padding: 0;
150 padding: 0;
151 }
151 }
152
152
153 div.options a {
153 div.options a {
154 height: 1%;
154 height: 1%;
155 display: block;
155 display: block;
156 text-decoration: none;
156 text-decoration: none;
157 margin: 0;
157 margin: 0;
158 padding: 3px 8px;
158 padding: 3px 8px;
159 }
159 }
160
160
161 .top-left-rounded-corner {
161 .top-left-rounded-corner {
162 -webkit-border-top-left-radius: 8px;
162 -webkit-border-top-left-radius: 8px;
163 -khtml-border-radius-topleft: 8px;
163 -khtml-border-radius-topleft: 8px;
164 -moz-border-radius-topleft: 8px;
164 -moz-border-radius-topleft: 8px;
165 border-top-left-radius: 8px;
165 border-top-left-radius: 8px;
166 }
166 }
167
167
168 .top-right-rounded-corner {
168 .top-right-rounded-corner {
169 -webkit-border-top-right-radius: 8px;
169 -webkit-border-top-right-radius: 8px;
170 -khtml-border-radius-topright: 8px;
170 -khtml-border-radius-topright: 8px;
171 -moz-border-radius-topright: 8px;
171 -moz-border-radius-topright: 8px;
172 border-top-right-radius: 8px;
172 border-top-right-radius: 8px;
173 }
173 }
174
174
175 .bottom-left-rounded-corner {
175 .bottom-left-rounded-corner {
176 -webkit-border-bottom-left-radius: 8px;
176 -webkit-border-bottom-left-radius: 8px;
177 -khtml-border-radius-bottomleft: 8px;
177 -khtml-border-radius-bottomleft: 8px;
178 -moz-border-radius-bottomleft: 8px;
178 -moz-border-radius-bottomleft: 8px;
179 border-bottom-left-radius: 8px;
179 border-bottom-left-radius: 8px;
180 }
180 }
181
181
182 .bottom-right-rounded-corner {
182 .bottom-right-rounded-corner {
183 -webkit-border-bottom-right-radius: 8px;
183 -webkit-border-bottom-right-radius: 8px;
184 -khtml-border-radius-bottomright: 8px;
184 -khtml-border-radius-bottomright: 8px;
185 -moz-border-radius-bottomright: 8px;
185 -moz-border-radius-bottomright: 8px;
186 border-bottom-right-radius: 8px;
186 border-bottom-right-radius: 8px;
187 }
187 }
188
188
189 .top-left-rounded-corner-mid {
189 .top-left-rounded-corner-mid {
190 -webkit-border-top-left-radius: 4px;
190 -webkit-border-top-left-radius: 4px;
191 -khtml-border-radius-topleft: 4px;
191 -khtml-border-radius-topleft: 4px;
192 -moz-border-radius-topleft: 4px;
192 -moz-border-radius-topleft: 4px;
193 border-top-left-radius: 4px;
193 border-top-left-radius: 4px;
194 }
194 }
195
195
196 .top-right-rounded-corner-mid {
196 .top-right-rounded-corner-mid {
197 -webkit-border-top-right-radius: 4px;
197 -webkit-border-top-right-radius: 4px;
198 -khtml-border-radius-topright: 4px;
198 -khtml-border-radius-topright: 4px;
199 -moz-border-radius-topright: 4px;
199 -moz-border-radius-topright: 4px;
200 border-top-right-radius: 4px;
200 border-top-right-radius: 4px;
201 }
201 }
202
202
203 .bottom-left-rounded-corner-mid {
203 .bottom-left-rounded-corner-mid {
204 -webkit-border-bottom-left-radius: 4px;
204 -webkit-border-bottom-left-radius: 4px;
205 -khtml-border-radius-bottomleft: 4px;
205 -khtml-border-radius-bottomleft: 4px;
206 -moz-border-radius-bottomleft: 4px;
206 -moz-border-radius-bottomleft: 4px;
207 border-bottom-left-radius: 4px;
207 border-bottom-left-radius: 4px;
208 }
208 }
209
209
210 .bottom-right-rounded-corner-mid {
210 .bottom-right-rounded-corner-mid {
211 -webkit-border-bottom-right-radius: 4px;
211 -webkit-border-bottom-right-radius: 4px;
212 -khtml-border-radius-bottomright: 4px;
212 -khtml-border-radius-bottomright: 4px;
213 -moz-border-radius-bottomright: 4px;
213 -moz-border-radius-bottomright: 4px;
214 border-bottom-right-radius: 4px;
214 border-bottom-right-radius: 4px;
215 }
215 }
216
216
217 .help-block {
217 .help-block {
218 color: #999999;
218 color: #999999;
219 display: block;
219 display: block;
220 margin-bottom: 0;
220 margin-bottom: 0;
221 margin-top: 5px;
221 margin-top: 5px;
222 }
222 }
223
223
224 .empty_data{
224 .empty_data{
225 color:#B9B9B9;
225 color:#B9B9B9;
226 }
226 }
227
227
228 a.permalink{
228 a.permalink{
229 visibility: hidden;
229 visibility: hidden;
230 }
230 }
231
231
232 a.permalink:hover{
232 a.permalink:hover{
233 text-decoration: none;
233 text-decoration: none;
234 }
234 }
235
235
236 h1:hover > a.permalink,
236 h1:hover > a.permalink,
237 h2:hover > a.permalink,
237 h2:hover > a.permalink,
238 h3:hover > a.permalink,
238 h3:hover > a.permalink,
239 h4:hover > a.permalink,
239 h4:hover > a.permalink,
240 h5:hover > a.permalink,
240 h5:hover > a.permalink,
241 h6:hover > a.permalink,
241 h6:hover > a.permalink,
242 div:hover > a.permalink {
242 div:hover > a.permalink {
243 visibility: visible;
243 visibility: visible;
244 }
244 }
245
245
246 #header {
246 #header {
247 margin: 0;
247 margin: 0;
248 padding: 0 10px;
248 padding: 0 10px;
249 }
249 }
250
250
251 #header ul#logged-user {
251 #header ul#logged-user {
252 margin-bottom: 5px !important;
252 margin-bottom: 5px !important;
253 -webkit-border-radius: 0px 0px 8px 8px;
253 -webkit-border-radius: 0px 0px 8px 8px;
254 -khtml-border-radius: 0px 0px 8px 8px;
254 -khtml-border-radius: 0px 0px 8px 8px;
255 -moz-border-radius: 0px 0px 8px 8px;
255 -moz-border-radius: 0px 0px 8px 8px;
256 border-radius: 0px 0px 8px 8px;
256 border-radius: 0px 0px 8px 8px;
257 height: 37px;
257 height: 37px;
258 background-color: #003B76;
258 background-color: #003B76;
259 background-repeat: repeat-x;
259 background-repeat: repeat-x;
260 background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
260 background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
261 background-image: -moz-linear-gradient(top, #003b76, #00376e);
261 background-image: -moz-linear-gradient(top, #003b76, #00376e);
262 background-image: -ms-linear-gradient(top, #003b76, #00376e);
262 background-image: -ms-linear-gradient(top, #003b76, #00376e);
263 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76), color-stop(100%, #00376e) );
263 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76), color-stop(100%, #00376e) );
264 background-image: -webkit-linear-gradient(top, #003b76, #00376e);
264 background-image: -webkit-linear-gradient(top, #003b76, #00376e);
265 background-image: -o-linear-gradient(top, #003b76, #00376e);
265 background-image: -o-linear-gradient(top, #003b76, #00376e);
266 background-image: linear-gradient(top, #003b76, #00376e);
266 background-image: linear-gradient(top, #003b76, #00376e);
267 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',endColorstr='#00376e', GradientType=0 );
267 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',endColorstr='#00376e', GradientType=0 );
268 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
268 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
269 }
269 }
270
270
271 #header ul#logged-user li {
271 #header ul#logged-user li {
272 list-style: none;
272 list-style: none;
273 float: left;
273 float: left;
274 margin: 8px 0 0;
274 margin: 8px 0 0;
275 padding: 4px 12px;
275 padding: 4px 12px;
276 border-left: 1px solid #316293;
276 border-left: 1px solid #316293;
277 }
277 }
278
278
279 #header ul#logged-user li.first {
279 #header ul#logged-user li.first {
280 border-left: none;
280 border-left: none;
281 margin: 4px;
281 margin: 4px;
282 }
282 }
283
283
284 #header ul#logged-user li.first div.gravatar {
284 #header ul#logged-user li.first div.gravatar {
285 margin-top: -2px;
285 margin-top: -2px;
286 }
286 }
287
287
288 #header ul#logged-user li.first div.account {
288 #header ul#logged-user li.first div.account {
289 padding-top: 4px;
289 padding-top: 4px;
290 float: left;
290 float: left;
291 }
291 }
292
292
293 #header ul#logged-user li.last {
293 #header ul#logged-user li.last {
294 border-right: none;
294 border-right: none;
295 }
295 }
296
296
297 #header ul#logged-user li a {
297 #header ul#logged-user li a {
298 color: #fff;
298 color: #fff;
299 font-weight: 700;
299 font-weight: 700;
300 text-decoration: none;
300 text-decoration: none;
301 }
301 }
302
302
303 #header ul#logged-user li a:hover {
303 #header ul#logged-user li a:hover {
304 text-decoration: underline;
304 text-decoration: underline;
305 }
305 }
306
306
307 #header ul#logged-user li.highlight a {
307 #header ul#logged-user li.highlight a {
308 color: #fff;
308 color: #fff;
309 }
309 }
310
310
311 #header ul#logged-user li.highlight a:hover {
311 #header ul#logged-user li.highlight a:hover {
312 color: #FFF;
312 color: #FFF;
313 }
313 }
314
314
315 #header #header-inner {
315 #header #header-inner {
316 min-height: 44px;
316 min-height: 44px;
317 clear: both;
317 clear: both;
318 position: relative;
318 position: relative;
319 background-color: #003B76;
319 background-color: #003B76;
320 background-repeat: repeat-x;
320 background-repeat: repeat-x;
321 background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
321 background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
322 background-image: -moz-linear-gradient(top, #003b76, #00376e);
322 background-image: -moz-linear-gradient(top, #003b76, #00376e);
323 background-image: -ms-linear-gradient(top, #003b76, #00376e);
323 background-image: -ms-linear-gradient(top, #003b76, #00376e);
324 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76),color-stop(100%, #00376e) );
324 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76),color-stop(100%, #00376e) );
325 background-image: -webkit-linear-gradient(top, #003b76, #00376e);
325 background-image: -webkit-linear-gradient(top, #003b76, #00376e);
326 background-image: -o-linear-gradient(top, #003b76, #00376e);
326 background-image: -o-linear-gradient(top, #003b76, #00376e);
327 background-image: linear-gradient(top, #003b76, #00376e);
327 background-image: linear-gradient(top, #003b76, #00376e);
328 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',endColorstr='#00376e', GradientType=0 );
328 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',endColorstr='#00376e', GradientType=0 );
329 margin: 0;
329 margin: 0;
330 padding: 0;
330 padding: 0;
331 display: block;
331 display: block;
332 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
332 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
333 -webkit-border-radius: 4px 4px 4px 4px;
333 -webkit-border-radius: 4px 4px 4px 4px;
334 -khtml-border-radius: 4px 4px 4px 4px;
334 -khtml-border-radius: 4px 4px 4px 4px;
335 -moz-border-radius: 4px 4px 4px 4px;
335 -moz-border-radius: 4px 4px 4px 4px;
336 border-radius: 4px 4px 4px 4px;
336 border-radius: 4px 4px 4px 4px;
337 }
337 }
338 #header #header-inner.hover{
338 #header #header-inner.hover{
339 position: fixed !important;
339 position: fixed !important;
340 width: 100% !important;
340 width: 100% !important;
341 margin-left: -10px !important;
341 margin-left: -10px !important;
342 z-index: 10000;
342 z-index: 10000;
343 -webkit-border-radius: 0px 0px 0px 0px;
343 -webkit-border-radius: 0px 0px 0px 0px;
344 -khtml-border-radius: 0px 0px 0px 0px;
344 -khtml-border-radius: 0px 0px 0px 0px;
345 -moz-border-radius: 0px 0px 0px 0px;
345 -moz-border-radius: 0px 0px 0px 0px;
346 border-radius: 0px 0px 0px 0px;
346 border-radius: 0px 0px 0px 0px;
347 }
347 }
348
348
349 .ie7 #header #header-inner.hover,
349 .ie7 #header #header-inner.hover,
350 .ie8 #header #header-inner.hover,
350 .ie8 #header #header-inner.hover,
351 .ie9 #header #header-inner.hover
351 .ie9 #header #header-inner.hover
352 {
352 {
353 z-index: auto !important;
353 z-index: auto !important;
354 }
354 }
355
355
356 .header-pos-fix, .anchor{
356 .header-pos-fix, .anchor{
357 margin-top: -46px;
357 margin-top: -46px;
358 padding-top: 46px;
358 padding-top: 46px;
359 }
359 }
360
360
361 #header #header-inner #home a {
361 #header #header-inner #home a {
362 height: 40px;
362 height: 40px;
363 width: 46px;
363 width: 46px;
364 display: block;
364 display: block;
365 background: url("../images/button_home.png");
365 background: url("../images/button_home.png");
366 background-position: 0 0;
366 background-position: 0 0;
367 margin: 0;
367 margin: 0;
368 padding: 0;
368 padding: 0;
369 }
369 }
370
370
371 #header #header-inner #home a:hover {
371 #header #header-inner #home a:hover {
372 background-position: 0 -40px;
372 background-position: 0 -40px;
373 }
373 }
374
374
375 #header #header-inner #logo {
375 #header #header-inner #logo {
376 float: left;
376 float: left;
377 position: absolute;
377 position: absolute;
378 }
378 }
379
379
380 #header #header-inner #logo h1 {
380 #header #header-inner #logo h1 {
381 color: #FFF;
381 color: #FFF;
382 font-size: 20px;
382 font-size: 20px;
383 margin: 12px 0 0 13px;
383 margin: 12px 0 0 13px;
384 padding: 0;
384 padding: 0;
385 }
385 }
386
386
387 #header #header-inner #logo a {
387 #header #header-inner #logo a {
388 color: #fff;
388 color: #fff;
389 text-decoration: none;
389 text-decoration: none;
390 }
390 }
391
391
392 #header #header-inner #logo a:hover {
392 #header #header-inner #logo a:hover {
393 color: #bfe3ff;
393 color: #bfe3ff;
394 }
394 }
395
395
396 #header #header-inner #quick,#header #header-inner #quick ul {
396 #header #header-inner #quick,#header #header-inner #quick ul {
397 position: relative;
397 position: relative;
398 float: right;
398 float: right;
399 list-style-type: none;
399 list-style-type: none;
400 list-style-position: outside;
400 list-style-position: outside;
401 margin: 8px 8px 0 0;
401 margin: 8px 8px 0 0;
402 padding: 0;
402 padding: 0;
403 }
403 }
404
404
405 #header #header-inner #quick li {
405 #header #header-inner #quick li {
406 position: relative;
406 position: relative;
407 float: left;
407 float: left;
408 margin: 0 5px 0 0;
408 margin: 0 5px 0 0;
409 padding: 0;
409 padding: 0;
410 }
410 }
411
411
412 #header #header-inner #quick li a.menu_link {
412 #header #header-inner #quick li a.menu_link {
413 top: 0;
413 top: 0;
414 left: 0;
414 left: 0;
415 height: 1%;
415 height: 1%;
416 display: block;
416 display: block;
417 clear: both;
417 clear: both;
418 overflow: hidden;
418 overflow: hidden;
419 color: #FFF;
419 color: #FFF;
420 font-weight: 700;
420 font-weight: 700;
421 text-decoration: none;
421 text-decoration: none;
422 background: #369;
422 background: #369;
423 padding: 0;
423 padding: 0;
424 -webkit-border-radius: 4px 4px 4px 4px;
424 -webkit-border-radius: 4px 4px 4px 4px;
425 -khtml-border-radius: 4px 4px 4px 4px;
425 -khtml-border-radius: 4px 4px 4px 4px;
426 -moz-border-radius: 4px 4px 4px 4px;
426 -moz-border-radius: 4px 4px 4px 4px;
427 border-radius: 4px 4px 4px 4px;
427 border-radius: 4px 4px 4px 4px;
428 }
428 }
429
429
430 #header #header-inner #quick li span.short {
430 #header #header-inner #quick li span.short {
431 padding: 9px 6px 8px 6px;
431 padding: 9px 6px 8px 6px;
432 }
432 }
433
433
434 #header #header-inner #quick li span {
434 #header #header-inner #quick li span {
435 top: 0;
435 top: 0;
436 right: 0;
436 right: 0;
437 height: 1%;
437 height: 1%;
438 display: block;
438 display: block;
439 float: left;
439 float: left;
440 border-left: 1px solid #3f6f9f;
440 border-left: 1px solid #3f6f9f;
441 margin: 0;
441 margin: 0;
442 padding: 10px 12px 8px 10px;
442 padding: 10px 12px 8px 10px;
443 }
443 }
444
444
445 #header #header-inner #quick li span.normal {
445 #header #header-inner #quick li span.normal {
446 border: none;
446 border: none;
447 padding: 10px 12px 8px;
447 padding: 10px 12px 8px;
448 }
448 }
449
449
450 #header #header-inner #quick li span.icon {
450 #header #header-inner #quick li span.icon {
451 top: 0;
451 top: 0;
452 left: 0;
452 left: 0;
453 border-left: none;
453 border-left: none;
454 border-right: 1px solid #2e5c89;
454 border-right: 1px solid #2e5c89;
455 padding: 8px 6px 4px;
455 padding: 8px 6px 4px;
456 }
456 }
457
457
458 #header #header-inner #quick li span.icon_short {
458 #header #header-inner #quick li span.icon_short {
459 top: 0;
459 top: 0;
460 left: 0;
460 left: 0;
461 border-left: none;
461 border-left: none;
462 border-right: 1px solid #2e5c89;
462 border-right: 1px solid #2e5c89;
463 padding: 8px 6px 4px;
463 padding: 8px 6px 4px;
464 }
464 }
465
465
466 #header #header-inner #quick li span.icon img,#header #header-inner #quick li span.icon_short img
466 #header #header-inner #quick li span.icon img,#header #header-inner #quick li span.icon_short img
467 {
467 {
468 margin: 0px -2px 0px 0px;
468 margin: 0px -2px 0px 0px;
469 }
469 }
470
470
471 #header #header-inner #quick li a:hover {
471 #header #header-inner #quick li a:hover {
472 background: #4e4e4e no-repeat top left;
472 background: #4e4e4e no-repeat top left;
473 }
473 }
474
474
475 #header #header-inner #quick li a:hover span {
475 #header #header-inner #quick li a:hover span {
476 border-left: 1px solid #545454;
476 border-left: 1px solid #545454;
477 }
477 }
478
478
479 #header #header-inner #quick li a:hover span.icon,#header #header-inner #quick li a:hover span.icon_short
479 #header #header-inner #quick li a:hover span.icon,#header #header-inner #quick li a:hover span.icon_short
480 {
480 {
481 border-left: none;
481 border-left: none;
482 border-right: 1px solid #464646;
482 border-right: 1px solid #464646;
483 }
483 }
484
484
485 #header #header-inner #quick ul {
485 #header #header-inner #quick ul {
486 top: 29px;
486 top: 29px;
487 right: 0;
487 right: 0;
488 min-width: 200px;
488 min-width: 200px;
489 display: none;
489 display: none;
490 position: absolute;
490 position: absolute;
491 background: #FFF;
491 background: #FFF;
492 border: 1px solid #666;
492 border: 1px solid #666;
493 border-top: 1px solid #003367;
493 border-top: 1px solid #003367;
494 z-index: 100;
494 z-index: 100;
495 margin: 0px 0px 0px 0px;
495 margin: 0px 0px 0px 0px;
496 padding: 0;
496 padding: 0;
497 }
497 }
498
498
499 #header #header-inner #quick ul.repo_switcher {
499 #header #header-inner #quick ul.repo_switcher {
500 max-height: 275px;
500 max-height: 275px;
501 overflow-x: hidden;
501 overflow-x: hidden;
502 overflow-y: auto;
502 overflow-y: auto;
503 }
503 }
504
504
505 #header #header-inner #quick ul.repo_switcher li.qfilter_rs {
505 #header #header-inner #quick ul.repo_switcher li.qfilter_rs {
506 float: none;
506 float: none;
507 margin: 0;
507 margin: 0;
508 border-bottom: 2px solid #003367;
508 border-bottom: 2px solid #003367;
509 }
509 }
510
510
511 #header #header-inner #quick .repo_switcher_type {
511 #header #header-inner #quick .repo_switcher_type {
512 position: absolute;
512 position: absolute;
513 left: 0;
513 left: 0;
514 top: 9px;
514 top: 9px;
515 }
515 }
516
516
517 #header #header-inner #quick li ul li {
517 #header #header-inner #quick li ul li {
518 border-bottom: 1px solid #ddd;
518 border-bottom: 1px solid #ddd;
519 }
519 }
520
520
521 #header #header-inner #quick li ul li a {
521 #header #header-inner #quick li ul li a {
522 width: 182px;
522 width: 182px;
523 height: auto;
523 height: auto;
524 display: block;
524 display: block;
525 float: left;
525 float: left;
526 background: #FFF;
526 background: #FFF;
527 color: #003367;
527 color: #003367;
528 font-weight: 400;
528 font-weight: 400;
529 margin: 0;
529 margin: 0;
530 padding: 7px 9px;
530 padding: 7px 9px;
531 }
531 }
532
532
533 #header #header-inner #quick li ul li a:hover {
533 #header #header-inner #quick li ul li a:hover {
534 color: #000;
534 color: #000;
535 background: #FFF;
535 background: #FFF;
536 }
536 }
537
537
538 #header #header-inner #quick ul ul {
538 #header #header-inner #quick ul ul {
539 top: auto;
539 top: auto;
540 }
540 }
541
541
542 #header #header-inner #quick li ul ul {
542 #header #header-inner #quick li ul ul {
543 right: 200px;
543 right: 200px;
544 max-height: 275px;
544 max-height: 275px;
545 overflow: auto;
545 overflow: auto;
546 overflow-x: hidden;
546 overflow-x: hidden;
547 white-space: normal;
547 white-space: normal;
548 }
548 }
549
549
550 #header #header-inner #quick li ul li a.journal,#header #header-inner #quick li ul li a.journal:hover
550 #header #header-inner #quick li ul li a.journal,#header #header-inner #quick li ul li a.journal:hover
551 {
551 {
552 background: url("../images/icons/book.png") no-repeat scroll 4px 9px
552 background: url("../images/icons/book.png") no-repeat scroll 4px 9px
553 #FFF;
553 #FFF;
554 width: 167px;
554 width: 167px;
555 margin: 0;
555 margin: 0;
556 padding: 12px 9px 7px 24px;
556 padding: 12px 9px 7px 24px;
557 }
557 }
558
558
559 #header #header-inner #quick li ul li a.private_repo,#header #header-inner #quick li ul li a.private_repo:hover
559 #header #header-inner #quick li ul li a.private_repo,#header #header-inner #quick li ul li a.private_repo:hover
560 {
560 {
561 background: url("../images/icons/lock.png") no-repeat scroll 4px 9px
561 background: url("../images/icons/lock.png") no-repeat scroll 4px 9px
562 #FFF;
562 #FFF;
563 min-width: 167px;
563 min-width: 167px;
564 margin: 0;
564 margin: 0;
565 padding: 12px 9px 7px 24px;
565 padding: 12px 9px 7px 24px;
566 }
566 }
567
567
568 #header #header-inner #quick li ul li a.public_repo,#header #header-inner #quick li ul li a.public_repo:hover
568 #header #header-inner #quick li ul li a.public_repo,#header #header-inner #quick li ul li a.public_repo:hover
569 {
569 {
570 background: url("../images/icons/lock_open.png") no-repeat scroll 4px
570 background: url("../images/icons/lock_open.png") no-repeat scroll 4px
571 9px #FFF;
571 9px #FFF;
572 min-width: 167px;
572 min-width: 167px;
573 margin: 0;
573 margin: 0;
574 padding: 12px 9px 7px 24px;
574 padding: 12px 9px 7px 24px;
575 }
575 }
576
576
577 #header #header-inner #quick li ul li a.hg,#header #header-inner #quick li ul li a.hg:hover
577 #header #header-inner #quick li ul li a.hg,#header #header-inner #quick li ul li a.hg:hover
578 {
578 {
579 background: url("../images/icons/hgicon.png") no-repeat scroll 4px 9px
579 background: url("../images/icons/hgicon.png") no-repeat scroll 4px 9px
580 #FFF;
580 #FFF;
581 min-width: 167px;
581 min-width: 167px;
582 margin: 0 0 0 14px;
582 margin: 0 0 0 14px;
583 padding: 12px 9px 7px 24px;
583 padding: 12px 9px 7px 24px;
584 }
584 }
585
585
586 #header #header-inner #quick li ul li a.git,#header #header-inner #quick li ul li a.git:hover
586 #header #header-inner #quick li ul li a.git,#header #header-inner #quick li ul li a.git:hover
587 {
587 {
588 background: url("../images/icons/giticon.png") no-repeat scroll 4px 9px
588 background: url("../images/icons/giticon.png") no-repeat scroll 4px 9px
589 #FFF;
589 #FFF;
590 min-width: 167px;
590 min-width: 167px;
591 margin: 0 0 0 14px;
591 margin: 0 0 0 14px;
592 padding: 12px 9px 7px 24px;
592 padding: 12px 9px 7px 24px;
593 }
593 }
594
594
595 #header #header-inner #quick li ul li a.repos,#header #header-inner #quick li ul li a.repos:hover
595 #header #header-inner #quick li ul li a.repos,#header #header-inner #quick li ul li a.repos:hover
596 {
596 {
597 background: url("../images/icons/database_edit.png") no-repeat scroll
597 background: url("../images/icons/database_edit.png") no-repeat scroll
598 4px 9px #FFF;
598 4px 9px #FFF;
599 width: 167px;
599 width: 167px;
600 margin: 0;
600 margin: 0;
601 padding: 12px 9px 7px 24px;
601 padding: 12px 9px 7px 24px;
602 }
602 }
603
603
604 #header #header-inner #quick li ul li a.repos_groups,#header #header-inner #quick li ul li a.repos_groups:hover
604 #header #header-inner #quick li ul li a.repos_groups,#header #header-inner #quick li ul li a.repos_groups:hover
605 {
605 {
606 background: url("../images/icons/database_link.png") no-repeat scroll
606 background: url("../images/icons/database_link.png") no-repeat scroll
607 4px 9px #FFF;
607 4px 9px #FFF;
608 width: 167px;
608 width: 167px;
609 margin: 0;
609 margin: 0;
610 padding: 12px 9px 7px 24px;
610 padding: 12px 9px 7px 24px;
611 }
611 }
612
612
613 #header #header-inner #quick li ul li a.users,#header #header-inner #quick li ul li a.users:hover
613 #header #header-inner #quick li ul li a.users,#header #header-inner #quick li ul li a.users:hover
614 {
614 {
615 background: #FFF url("../images/icons/user_edit.png") no-repeat 4px 9px;
615 background: #FFF url("../images/icons/user_edit.png") no-repeat 4px 9px;
616 width: 167px;
616 width: 167px;
617 margin: 0;
617 margin: 0;
618 padding: 12px 9px 7px 24px;
618 padding: 12px 9px 7px 24px;
619 }
619 }
620
620
621 #header #header-inner #quick li ul li a.groups,#header #header-inner #quick li ul li a.groups:hover
621 #header #header-inner #quick li ul li a.groups,#header #header-inner #quick li ul li a.groups:hover
622 {
622 {
623 background: #FFF url("../images/icons/group_edit.png") no-repeat 4px 9px;
623 background: #FFF url("../images/icons/group_edit.png") no-repeat 4px 9px;
624 width: 167px;
624 width: 167px;
625 margin: 0;
625 margin: 0;
626 padding: 12px 9px 7px 24px;
626 padding: 12px 9px 7px 24px;
627 }
627 }
628
628
629 #header #header-inner #quick li ul li a.defaults,#header #header-inner #quick li ul li a.defaults:hover
630 {
631 background: #FFF url("../images/icons/wrench.png") no-repeat 4px 9px;
632 width: 167px;
633 margin: 0;
634 padding: 12px 9px 7px 24px;
635 }
636
629 #header #header-inner #quick li ul li a.settings,#header #header-inner #quick li ul li a.settings:hover
637 #header #header-inner #quick li ul li a.settings,#header #header-inner #quick li ul li a.settings:hover
630 {
638 {
631 background: #FFF url("../images/icons/cog.png") no-repeat 4px 9px;
639 background: #FFF url("../images/icons/cog.png") no-repeat 4px 9px;
632 width: 167px;
640 width: 167px;
633 margin: 0;
641 margin: 0;
634 padding: 12px 9px 7px 24px;
642 padding: 12px 9px 7px 24px;
635 }
643 }
636
644
637 #header #header-inner #quick li ul li a.permissions,#header #header-inner #quick li ul li a.permissions:hover
645 #header #header-inner #quick li ul li a.permissions,#header #header-inner #quick li ul li a.permissions:hover
638 {
646 {
639 background: #FFF url("../images/icons/key.png") no-repeat 4px 9px;
647 background: #FFF url("../images/icons/key.png") no-repeat 4px 9px;
640 width: 167px;
648 width: 167px;
641 margin: 0;
649 margin: 0;
642 padding: 12px 9px 7px 24px;
650 padding: 12px 9px 7px 24px;
643 }
651 }
644
652
645 #header #header-inner #quick li ul li a.ldap,#header #header-inner #quick li ul li a.ldap:hover
653 #header #header-inner #quick li ul li a.ldap,#header #header-inner #quick li ul li a.ldap:hover
646 {
654 {
647 background: #FFF url("../images/icons/server_key.png") no-repeat 4px 9px;
655 background: #FFF url("../images/icons/server_key.png") no-repeat 4px 9px;
648 width: 167px;
656 width: 167px;
649 margin: 0;
657 margin: 0;
650 padding: 12px 9px 7px 24px;
658 padding: 12px 9px 7px 24px;
651 }
659 }
652
660
653 #header #header-inner #quick li ul li a.fork,#header #header-inner #quick li ul li a.fork:hover
661 #header #header-inner #quick li ul li a.fork,#header #header-inner #quick li ul li a.fork:hover
654 {
662 {
655 background: #FFF url("../images/icons/arrow_divide.png") no-repeat 4px
663 background: #FFF url("../images/icons/arrow_divide.png") no-repeat 4px
656 9px;
664 9px;
657 width: 167px;
665 width: 167px;
658 margin: 0;
666 margin: 0;
659 padding: 12px 9px 7px 24px;
667 padding: 12px 9px 7px 24px;
660 }
668 }
661
669
662 #header #header-inner #quick li ul li a.locking_add,#header #header-inner #quick li ul li a.locking_add:hover
670 #header #header-inner #quick li ul li a.locking_add,#header #header-inner #quick li ul li a.locking_add:hover
663 {
671 {
664 background: #FFF url("../images/icons/lock_add.png") no-repeat 4px
672 background: #FFF url("../images/icons/lock_add.png") no-repeat 4px
665 9px;
673 9px;
666 width: 167px;
674 width: 167px;
667 margin: 0;
675 margin: 0;
668 padding: 12px 9px 7px 24px;
676 padding: 12px 9px 7px 24px;
669 }
677 }
670
678
671 #header #header-inner #quick li ul li a.locking_del,#header #header-inner #quick li ul li a.locking_del:hover
679 #header #header-inner #quick li ul li a.locking_del,#header #header-inner #quick li ul li a.locking_del:hover
672 {
680 {
673 background: #FFF url("../images/icons/lock_delete.png") no-repeat 4px
681 background: #FFF url("../images/icons/lock_delete.png") no-repeat 4px
674 9px;
682 9px;
675 width: 167px;
683 width: 167px;
676 margin: 0;
684 margin: 0;
677 padding: 12px 9px 7px 24px;
685 padding: 12px 9px 7px 24px;
678 }
686 }
679
687
680 #header #header-inner #quick li ul li a.pull_request,#header #header-inner #quick li ul li a.pull_request:hover
688 #header #header-inner #quick li ul li a.pull_request,#header #header-inner #quick li ul li a.pull_request:hover
681 {
689 {
682 background: #FFF url("../images/icons/arrow_join.png") no-repeat 4px
690 background: #FFF url("../images/icons/arrow_join.png") no-repeat 4px
683 9px;
691 9px;
684 width: 167px;
692 width: 167px;
685 margin: 0;
693 margin: 0;
686 padding: 12px 9px 7px 24px;
694 padding: 12px 9px 7px 24px;
687 }
695 }
688
696
689 #header #header-inner #quick li ul li a.compare_request,#header #header-inner #quick li ul li a.compare_request:hover
697 #header #header-inner #quick li ul li a.compare_request,#header #header-inner #quick li ul li a.compare_request:hover
690 {
698 {
691 background: #FFF url("../images/icons/arrow_inout.png") no-repeat 4px
699 background: #FFF url("../images/icons/arrow_inout.png") no-repeat 4px
692 9px;
700 9px;
693 width: 167px;
701 width: 167px;
694 margin: 0;
702 margin: 0;
695 padding: 12px 9px 7px 24px;
703 padding: 12px 9px 7px 24px;
696 }
704 }
697
705
698 #header #header-inner #quick li ul li a.search,#header #header-inner #quick li ul li a.search:hover
706 #header #header-inner #quick li ul li a.search,#header #header-inner #quick li ul li a.search:hover
699 {
707 {
700 background: #FFF url("../images/icons/search_16.png") no-repeat 4px 9px;
708 background: #FFF url("../images/icons/search_16.png") no-repeat 4px 9px;
701 width: 167px;
709 width: 167px;
702 margin: 0;
710 margin: 0;
703 padding: 12px 9px 7px 24px;
711 padding: 12px 9px 7px 24px;
704 }
712 }
705
713
706 #header #header-inner #quick li ul li a.delete,#header #header-inner #quick li ul li a.delete:hover
714 #header #header-inner #quick li ul li a.delete,#header #header-inner #quick li ul li a.delete:hover
707 {
715 {
708 background: #FFF url("../images/icons/delete.png") no-repeat 4px 9px;
716 background: #FFF url("../images/icons/delete.png") no-repeat 4px 9px;
709 width: 167px;
717 width: 167px;
710 margin: 0;
718 margin: 0;
711 padding: 12px 9px 7px 24px;
719 padding: 12px 9px 7px 24px;
712 }
720 }
713
721
714 #header #header-inner #quick li ul li a.branches,#header #header-inner #quick li ul li a.branches:hover
722 #header #header-inner #quick li ul li a.branches,#header #header-inner #quick li ul li a.branches:hover
715 {
723 {
716 background: #FFF url("../images/icons/arrow_branch.png") no-repeat 4px
724 background: #FFF url("../images/icons/arrow_branch.png") no-repeat 4px
717 9px;
725 9px;
718 width: 167px;
726 width: 167px;
719 margin: 0;
727 margin: 0;
720 padding: 12px 9px 7px 24px;
728 padding: 12px 9px 7px 24px;
721 }
729 }
722
730
723 #header #header-inner #quick li ul li a.tags,
731 #header #header-inner #quick li ul li a.tags,
724 #header #header-inner #quick li ul li a.tags:hover{
732 #header #header-inner #quick li ul li a.tags:hover{
725 background: #FFF url("../images/icons/tag_blue.png") no-repeat 4px 9px;
733 background: #FFF url("../images/icons/tag_blue.png") no-repeat 4px 9px;
726 width: 167px;
734 width: 167px;
727 margin: 0;
735 margin: 0;
728 padding: 12px 9px 7px 24px;
736 padding: 12px 9px 7px 24px;
729 }
737 }
730
738
731 #header #header-inner #quick li ul li a.bookmarks,
739 #header #header-inner #quick li ul li a.bookmarks,
732 #header #header-inner #quick li ul li a.bookmarks:hover{
740 #header #header-inner #quick li ul li a.bookmarks:hover{
733 background: #FFF url("../images/icons/tag_green.png") no-repeat 4px 9px;
741 background: #FFF url("../images/icons/tag_green.png") no-repeat 4px 9px;
734 width: 167px;
742 width: 167px;
735 margin: 0;
743 margin: 0;
736 padding: 12px 9px 7px 24px;
744 padding: 12px 9px 7px 24px;
737 }
745 }
738
746
739 #header #header-inner #quick li ul li a.admin,
747 #header #header-inner #quick li ul li a.admin,
740 #header #header-inner #quick li ul li a.admin:hover{
748 #header #header-inner #quick li ul li a.admin:hover{
741 background: #FFF url("../images/icons/cog_edit.png") no-repeat 4px 9px;
749 background: #FFF url("../images/icons/cog_edit.png") no-repeat 4px 9px;
742 width: 167px;
750 width: 167px;
743 margin: 0;
751 margin: 0;
744 padding: 12px 9px 7px 24px;
752 padding: 12px 9px 7px 24px;
745 }
753 }
746
754
747 .groups_breadcrumbs a {
755 .groups_breadcrumbs a {
748 color: #fff;
756 color: #fff;
749 }
757 }
750
758
751 .groups_breadcrumbs a:hover {
759 .groups_breadcrumbs a:hover {
752 color: #bfe3ff;
760 color: #bfe3ff;
753 text-decoration: none;
761 text-decoration: none;
754 }
762 }
755
763
756 td.quick_repo_menu {
764 td.quick_repo_menu {
757 background: #FFF url("../images/vertical-indicator.png") 8px 50% no-repeat !important;
765 background: #FFF url("../images/vertical-indicator.png") 8px 50% no-repeat !important;
758 cursor: pointer;
766 cursor: pointer;
759 width: 8px;
767 width: 8px;
760 border: 1px solid transparent;
768 border: 1px solid transparent;
761 }
769 }
762
770
763 td.quick_repo_menu.active {
771 td.quick_repo_menu.active {
764 background: url("../images/dt-arrow-dn.png") no-repeat scroll 5px 50% #FFFFFF !important;
772 background: url("../images/dt-arrow-dn.png") no-repeat scroll 5px 50% #FFFFFF !important;
765 border: 1px solid #003367;
773 border: 1px solid #003367;
766 box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
774 box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
767 cursor: pointer;
775 cursor: pointer;
768 }
776 }
769
777
770 td.quick_repo_menu .menu_items {
778 td.quick_repo_menu .menu_items {
771 margin-top: 10px;
779 margin-top: 10px;
772 margin-left:-6px;
780 margin-left:-6px;
773 width: 150px;
781 width: 150px;
774 position: absolute;
782 position: absolute;
775 background-color: #FFF;
783 background-color: #FFF;
776 background: none repeat scroll 0 0 #FFFFFF;
784 background: none repeat scroll 0 0 #FFFFFF;
777 border-color: #003367 #666666 #666666;
785 border-color: #003367 #666666 #666666;
778 border-right: 1px solid #666666;
786 border-right: 1px solid #666666;
779 border-style: solid;
787 border-style: solid;
780 border-width: 1px;
788 border-width: 1px;
781 box-shadow: 2px 8px 4px rgba(0, 0, 0, 0.2);
789 box-shadow: 2px 8px 4px rgba(0, 0, 0, 0.2);
782 border-top-style: none;
790 border-top-style: none;
783 }
791 }
784
792
785 td.quick_repo_menu .menu_items li {
793 td.quick_repo_menu .menu_items li {
786 padding: 0 !important;
794 padding: 0 !important;
787 }
795 }
788
796
789 td.quick_repo_menu .menu_items a {
797 td.quick_repo_menu .menu_items a {
790 display: block;
798 display: block;
791 padding: 4px 12px 4px 8px;
799 padding: 4px 12px 4px 8px;
792 }
800 }
793
801
794 td.quick_repo_menu .menu_items a:hover {
802 td.quick_repo_menu .menu_items a:hover {
795 background-color: #EEE;
803 background-color: #EEE;
796 text-decoration: none;
804 text-decoration: none;
797 }
805 }
798
806
799 td.quick_repo_menu .menu_items .icon img {
807 td.quick_repo_menu .menu_items .icon img {
800 margin-bottom: -2px;
808 margin-bottom: -2px;
801 }
809 }
802
810
803 td.quick_repo_menu .menu_items.hidden {
811 td.quick_repo_menu .menu_items.hidden {
804 display: none;
812 display: none;
805 }
813 }
806
814
807 .yui-dt-first th {
815 .yui-dt-first th {
808 text-align: left;
816 text-align: left;
809 }
817 }
810
818
811 /*
819 /*
812 Copyright (c) 2011, Yahoo! Inc. All rights reserved.
820 Copyright (c) 2011, Yahoo! Inc. All rights reserved.
813 Code licensed under the BSD License:
821 Code licensed under the BSD License:
814 http://developer.yahoo.com/yui/license.html
822 http://developer.yahoo.com/yui/license.html
815 version: 2.9.0
823 version: 2.9.0
816 */
824 */
817 .yui-skin-sam .yui-dt-mask {
825 .yui-skin-sam .yui-dt-mask {
818 position: absolute;
826 position: absolute;
819 z-index: 9500;
827 z-index: 9500;
820 }
828 }
821 .yui-dt-tmp {
829 .yui-dt-tmp {
822 position: absolute;
830 position: absolute;
823 left: -9000px;
831 left: -9000px;
824 }
832 }
825 .yui-dt-scrollable .yui-dt-bd { overflow: auto }
833 .yui-dt-scrollable .yui-dt-bd { overflow: auto }
826 .yui-dt-scrollable .yui-dt-hd {
834 .yui-dt-scrollable .yui-dt-hd {
827 overflow: hidden;
835 overflow: hidden;
828 position: relative;
836 position: relative;
829 }
837 }
830 .yui-dt-scrollable .yui-dt-bd thead tr,
838 .yui-dt-scrollable .yui-dt-bd thead tr,
831 .yui-dt-scrollable .yui-dt-bd thead th {
839 .yui-dt-scrollable .yui-dt-bd thead th {
832 position: absolute;
840 position: absolute;
833 left: -1500px;
841 left: -1500px;
834 }
842 }
835 .yui-dt-scrollable tbody { -moz-outline: 0 }
843 .yui-dt-scrollable tbody { -moz-outline: 0 }
836 .yui-skin-sam thead .yui-dt-sortable { cursor: pointer }
844 .yui-skin-sam thead .yui-dt-sortable { cursor: pointer }
837 .yui-skin-sam thead .yui-dt-draggable { cursor: move }
845 .yui-skin-sam thead .yui-dt-draggable { cursor: move }
838 .yui-dt-coltarget {
846 .yui-dt-coltarget {
839 position: absolute;
847 position: absolute;
840 z-index: 999;
848 z-index: 999;
841 }
849 }
842 .yui-dt-hd { zoom: 1 }
850 .yui-dt-hd { zoom: 1 }
843 th.yui-dt-resizeable .yui-dt-resizerliner { position: relative }
851 th.yui-dt-resizeable .yui-dt-resizerliner { position: relative }
844 .yui-dt-resizer {
852 .yui-dt-resizer {
845 position: absolute;
853 position: absolute;
846 right: 0;
854 right: 0;
847 bottom: 0;
855 bottom: 0;
848 height: 100%;
856 height: 100%;
849 cursor: e-resize;
857 cursor: e-resize;
850 cursor: col-resize;
858 cursor: col-resize;
851 background-color: #CCC;
859 background-color: #CCC;
852 opacity: 0;
860 opacity: 0;
853 filter: alpha(opacity=0);
861 filter: alpha(opacity=0);
854 }
862 }
855 .yui-dt-resizerproxy {
863 .yui-dt-resizerproxy {
856 visibility: hidden;
864 visibility: hidden;
857 position: absolute;
865 position: absolute;
858 z-index: 9000;
866 z-index: 9000;
859 background-color: #CCC;
867 background-color: #CCC;
860 opacity: 0;
868 opacity: 0;
861 filter: alpha(opacity=0);
869 filter: alpha(opacity=0);
862 }
870 }
863 th.yui-dt-hidden .yui-dt-liner,
871 th.yui-dt-hidden .yui-dt-liner,
864 td.yui-dt-hidden .yui-dt-liner,
872 td.yui-dt-hidden .yui-dt-liner,
865 th.yui-dt-hidden .yui-dt-resizer { display: none }
873 th.yui-dt-hidden .yui-dt-resizer { display: none }
866 .yui-dt-editor,
874 .yui-dt-editor,
867 .yui-dt-editor-shim {
875 .yui-dt-editor-shim {
868 position: absolute;
876 position: absolute;
869 z-index: 9000;
877 z-index: 9000;
870 }
878 }
871 .yui-skin-sam .yui-dt table {
879 .yui-skin-sam .yui-dt table {
872 margin: 0;
880 margin: 0;
873 padding: 0;
881 padding: 0;
874 font-family: arial;
882 font-family: arial;
875 font-size: inherit;
883 font-size: inherit;
876 border-collapse: separate;
884 border-collapse: separate;
877 *border-collapse: collapse;
885 *border-collapse: collapse;
878 border-spacing: 0;
886 border-spacing: 0;
879 border: 1px solid #7f7f7f;
887 border: 1px solid #7f7f7f;
880 }
888 }
881 .yui-skin-sam .yui-dt thead { border-spacing: 0 }
889 .yui-skin-sam .yui-dt thead { border-spacing: 0 }
882 .yui-skin-sam .yui-dt caption {
890 .yui-skin-sam .yui-dt caption {
883 color: #000;
891 color: #000;
884 font-size: 85%;
892 font-size: 85%;
885 font-weight: normal;
893 font-weight: normal;
886 font-style: italic;
894 font-style: italic;
887 line-height: 1;
895 line-height: 1;
888 padding: 1em 0;
896 padding: 1em 0;
889 text-align: center;
897 text-align: center;
890 }
898 }
891 .yui-skin-sam .yui-dt th { background: #d8d8da url(../images/sprite.png) repeat-x 0 0 }
899 .yui-skin-sam .yui-dt th { background: #d8d8da url(../images/sprite.png) repeat-x 0 0 }
892 .yui-skin-sam .yui-dt th,
900 .yui-skin-sam .yui-dt th,
893 .yui-skin-sam .yui-dt th a {
901 .yui-skin-sam .yui-dt th a {
894 font-weight: normal;
902 font-weight: normal;
895 text-decoration: none;
903 text-decoration: none;
896 color: #000;
904 color: #000;
897 vertical-align: bottom;
905 vertical-align: bottom;
898 }
906 }
899 .yui-skin-sam .yui-dt th {
907 .yui-skin-sam .yui-dt th {
900 margin: 0;
908 margin: 0;
901 padding: 0;
909 padding: 0;
902 border: 0;
910 border: 0;
903 border-right: 1px solid #cbcbcb;
911 border-right: 1px solid #cbcbcb;
904 }
912 }
905 .yui-skin-sam .yui-dt tr.yui-dt-first td { border-top: 1px solid #7f7f7f }
913 .yui-skin-sam .yui-dt tr.yui-dt-first td { border-top: 1px solid #7f7f7f }
906 .yui-skin-sam .yui-dt th .yui-dt-liner { white-space: nowrap }
914 .yui-skin-sam .yui-dt th .yui-dt-liner { white-space: nowrap }
907 .yui-skin-sam .yui-dt-liner {
915 .yui-skin-sam .yui-dt-liner {
908 margin: 0;
916 margin: 0;
909 padding: 0;
917 padding: 0;
910 }
918 }
911 .yui-skin-sam .yui-dt-coltarget {
919 .yui-skin-sam .yui-dt-coltarget {
912 width: 5px;
920 width: 5px;
913 background-color: red;
921 background-color: red;
914 }
922 }
915 .yui-skin-sam .yui-dt td {
923 .yui-skin-sam .yui-dt td {
916 margin: 0;
924 margin: 0;
917 padding: 0;
925 padding: 0;
918 border: 0;
926 border: 0;
919 border-right: 1px solid #cbcbcb;
927 border-right: 1px solid #cbcbcb;
920 text-align: left;
928 text-align: left;
921 }
929 }
922 .yui-skin-sam .yui-dt-list td { border-right: 0 }
930 .yui-skin-sam .yui-dt-list td { border-right: 0 }
923 .yui-skin-sam .yui-dt-resizer { width: 6px }
931 .yui-skin-sam .yui-dt-resizer { width: 6px }
924 .yui-skin-sam .yui-dt-mask {
932 .yui-skin-sam .yui-dt-mask {
925 background-color: #000;
933 background-color: #000;
926 opacity: .25;
934 opacity: .25;
927 filter: alpha(opacity=25);
935 filter: alpha(opacity=25);
928 }
936 }
929 .yui-skin-sam .yui-dt-message { background-color: #FFF }
937 .yui-skin-sam .yui-dt-message { background-color: #FFF }
930 .yui-skin-sam .yui-dt-scrollable table { border: 0 }
938 .yui-skin-sam .yui-dt-scrollable table { border: 0 }
931 .yui-skin-sam .yui-dt-scrollable .yui-dt-hd {
939 .yui-skin-sam .yui-dt-scrollable .yui-dt-hd {
932 border-left: 1px solid #7f7f7f;
940 border-left: 1px solid #7f7f7f;
933 border-top: 1px solid #7f7f7f;
941 border-top: 1px solid #7f7f7f;
934 border-right: 1px solid #7f7f7f;
942 border-right: 1px solid #7f7f7f;
935 }
943 }
936 .yui-skin-sam .yui-dt-scrollable .yui-dt-bd {
944 .yui-skin-sam .yui-dt-scrollable .yui-dt-bd {
937 border-left: 1px solid #7f7f7f;
945 border-left: 1px solid #7f7f7f;
938 border-bottom: 1px solid #7f7f7f;
946 border-bottom: 1px solid #7f7f7f;
939 border-right: 1px solid #7f7f7f;
947 border-right: 1px solid #7f7f7f;
940 background-color: #FFF;
948 background-color: #FFF;
941 }
949 }
942 .yui-skin-sam .yui-dt-scrollable .yui-dt-data tr.yui-dt-last td { border-bottom: 1px solid #7f7f7f }
950 .yui-skin-sam .yui-dt-scrollable .yui-dt-data tr.yui-dt-last td { border-bottom: 1px solid #7f7f7f }
943 .yui-skin-sam th.yui-dt-asc,
951 .yui-skin-sam th.yui-dt-asc,
944 .yui-skin-sam th.yui-dt-desc { background: url(../images/sprite.png) repeat-x 0 -100px }
952 .yui-skin-sam th.yui-dt-desc { background: url(../images/sprite.png) repeat-x 0 -100px }
945 .yui-skin-sam th.yui-dt-sortable .yui-dt-label { margin-right: 10px }
953 .yui-skin-sam th.yui-dt-sortable .yui-dt-label { margin-right: 10px }
946 .yui-skin-sam th.yui-dt-asc .yui-dt-liner { background: url(../images/dt-arrow-up.png) no-repeat right }
954 .yui-skin-sam th.yui-dt-asc .yui-dt-liner { background: url(../images/dt-arrow-up.png) no-repeat right }
947 .yui-skin-sam th.yui-dt-desc .yui-dt-liner { background: url(../images/dt-arrow-dn.png) no-repeat right }
955 .yui-skin-sam th.yui-dt-desc .yui-dt-liner { background: url(../images/dt-arrow-dn.png) no-repeat right }
948 tbody .yui-dt-editable { cursor: pointer }
956 tbody .yui-dt-editable { cursor: pointer }
949 .yui-dt-editor {
957 .yui-dt-editor {
950 text-align: left;
958 text-align: left;
951 background-color: #f2f2f2;
959 background-color: #f2f2f2;
952 border: 1px solid #808080;
960 border: 1px solid #808080;
953 padding: 6px;
961 padding: 6px;
954 }
962 }
955 .yui-dt-editor label {
963 .yui-dt-editor label {
956 padding-left: 4px;
964 padding-left: 4px;
957 padding-right: 6px;
965 padding-right: 6px;
958 }
966 }
959 .yui-dt-editor .yui-dt-button {
967 .yui-dt-editor .yui-dt-button {
960 padding-top: 6px;
968 padding-top: 6px;
961 text-align: right;
969 text-align: right;
962 }
970 }
963 .yui-dt-editor .yui-dt-button button {
971 .yui-dt-editor .yui-dt-button button {
964 background: url(../images/sprite.png) repeat-x 0 0;
972 background: url(../images/sprite.png) repeat-x 0 0;
965 border: 1px solid #999;
973 border: 1px solid #999;
966 width: 4em;
974 width: 4em;
967 height: 1.8em;
975 height: 1.8em;
968 margin-left: 6px;
976 margin-left: 6px;
969 }
977 }
970 .yui-dt-editor .yui-dt-button button.yui-dt-default {
978 .yui-dt-editor .yui-dt-button button.yui-dt-default {
971 background: url(../images/sprite.png) repeat-x 0 -1400px;
979 background: url(../images/sprite.png) repeat-x 0 -1400px;
972 background-color: #5584e0;
980 background-color: #5584e0;
973 border: 1px solid #304369;
981 border: 1px solid #304369;
974 color: #FFF;
982 color: #FFF;
975 }
983 }
976 .yui-dt-editor .yui-dt-button button:hover {
984 .yui-dt-editor .yui-dt-button button:hover {
977 background: url(../images/sprite.png) repeat-x 0 -1300px;
985 background: url(../images/sprite.png) repeat-x 0 -1300px;
978 color: #000;
986 color: #000;
979 }
987 }
980 .yui-dt-editor .yui-dt-button button:active {
988 .yui-dt-editor .yui-dt-button button:active {
981 background: url(../images/sprite.png) repeat-x 0 -1700px;
989 background: url(../images/sprite.png) repeat-x 0 -1700px;
982 color: #000;
990 color: #000;
983 }
991 }
984 .yui-skin-sam tr.yui-dt-even { background-color: #FFF }
992 .yui-skin-sam tr.yui-dt-even { background-color: #FFF }
985 .yui-skin-sam tr.yui-dt-odd { background-color: #edf5ff }
993 .yui-skin-sam tr.yui-dt-odd { background-color: #edf5ff }
986 .yui-skin-sam tr.yui-dt-even td.yui-dt-asc,
994 .yui-skin-sam tr.yui-dt-even td.yui-dt-asc,
987 .yui-skin-sam tr.yui-dt-even td.yui-dt-desc { background-color: #edf5ff }
995 .yui-skin-sam tr.yui-dt-even td.yui-dt-desc { background-color: #edf5ff }
988 .yui-skin-sam tr.yui-dt-odd td.yui-dt-asc,
996 .yui-skin-sam tr.yui-dt-odd td.yui-dt-asc,
989 .yui-skin-sam tr.yui-dt-odd td.yui-dt-desc { background-color: #dbeaff }
997 .yui-skin-sam tr.yui-dt-odd td.yui-dt-desc { background-color: #dbeaff }
990 .yui-skin-sam .yui-dt-list tr.yui-dt-even { background-color: #FFF }
998 .yui-skin-sam .yui-dt-list tr.yui-dt-even { background-color: #FFF }
991 .yui-skin-sam .yui-dt-list tr.yui-dt-odd { background-color: #FFF }
999 .yui-skin-sam .yui-dt-list tr.yui-dt-odd { background-color: #FFF }
992 .yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-asc,
1000 .yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-asc,
993 .yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-desc { background-color: #edf5ff }
1001 .yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-desc { background-color: #edf5ff }
994 .yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-asc,
1002 .yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-asc,
995 .yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-desc { background-color: #edf5ff }
1003 .yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-desc { background-color: #edf5ff }
996 .yui-skin-sam th.yui-dt-highlighted,
1004 .yui-skin-sam th.yui-dt-highlighted,
997 .yui-skin-sam th.yui-dt-highlighted a { background-color: #b2d2ff }
1005 .yui-skin-sam th.yui-dt-highlighted a { background-color: #b2d2ff }
998 .yui-skin-sam tr.yui-dt-highlighted,
1006 .yui-skin-sam tr.yui-dt-highlighted,
999 .yui-skin-sam tr.yui-dt-highlighted td.yui-dt-asc,
1007 .yui-skin-sam tr.yui-dt-highlighted td.yui-dt-asc,
1000 .yui-skin-sam tr.yui-dt-highlighted td.yui-dt-desc,
1008 .yui-skin-sam tr.yui-dt-highlighted td.yui-dt-desc,
1001 .yui-skin-sam tr.yui-dt-even td.yui-dt-highlighted,
1009 .yui-skin-sam tr.yui-dt-even td.yui-dt-highlighted,
1002 .yui-skin-sam tr.yui-dt-odd td.yui-dt-highlighted {
1010 .yui-skin-sam tr.yui-dt-odd td.yui-dt-highlighted {
1003 cursor: pointer;
1011 cursor: pointer;
1004 background-color: #b2d2ff;
1012 background-color: #b2d2ff;
1005 }
1013 }
1006 .yui-skin-sam .yui-dt-list th.yui-dt-highlighted,
1014 .yui-skin-sam .yui-dt-list th.yui-dt-highlighted,
1007 .yui-skin-sam .yui-dt-list th.yui-dt-highlighted a { background-color: #b2d2ff }
1015 .yui-skin-sam .yui-dt-list th.yui-dt-highlighted a { background-color: #b2d2ff }
1008 .yui-skin-sam .yui-dt-list tr.yui-dt-highlighted,
1016 .yui-skin-sam .yui-dt-list tr.yui-dt-highlighted,
1009 .yui-skin-sam .yui-dt-list tr.yui-dt-highlighted td.yui-dt-asc,
1017 .yui-skin-sam .yui-dt-list tr.yui-dt-highlighted td.yui-dt-asc,
1010 .yui-skin-sam .yui-dt-list tr.yui-dt-highlighted td.yui-dt-desc,
1018 .yui-skin-sam .yui-dt-list tr.yui-dt-highlighted td.yui-dt-desc,
1011 .yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-highlighted,
1019 .yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-highlighted,
1012 .yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-highlighted {
1020 .yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-highlighted {
1013 cursor: pointer;
1021 cursor: pointer;
1014 background-color: #b2d2ff;
1022 background-color: #b2d2ff;
1015 }
1023 }
1016 .yui-skin-sam th.yui-dt-selected,
1024 .yui-skin-sam th.yui-dt-selected,
1017 .yui-skin-sam th.yui-dt-selected a { background-color: #446cd7 }
1025 .yui-skin-sam th.yui-dt-selected a { background-color: #446cd7 }
1018 .yui-skin-sam tr.yui-dt-selected td,
1026 .yui-skin-sam tr.yui-dt-selected td,
1019 .yui-skin-sam tr.yui-dt-selected td.yui-dt-asc,
1027 .yui-skin-sam tr.yui-dt-selected td.yui-dt-asc,
1020 .yui-skin-sam tr.yui-dt-selected td.yui-dt-desc {
1028 .yui-skin-sam tr.yui-dt-selected td.yui-dt-desc {
1021 background-color: #426fd9;
1029 background-color: #426fd9;
1022 color: #FFF;
1030 color: #FFF;
1023 }
1031 }
1024 .yui-skin-sam tr.yui-dt-even td.yui-dt-selected,
1032 .yui-skin-sam tr.yui-dt-even td.yui-dt-selected,
1025 .yui-skin-sam tr.yui-dt-odd td.yui-dt-selected {
1033 .yui-skin-sam tr.yui-dt-odd td.yui-dt-selected {
1026 background-color: #446cd7;
1034 background-color: #446cd7;
1027 color: #FFF;
1035 color: #FFF;
1028 }
1036 }
1029 .yui-skin-sam .yui-dt-list th.yui-dt-selected,
1037 .yui-skin-sam .yui-dt-list th.yui-dt-selected,
1030 .yui-skin-sam .yui-dt-list th.yui-dt-selected a { background-color: #446cd7 }
1038 .yui-skin-sam .yui-dt-list th.yui-dt-selected a { background-color: #446cd7 }
1031 .yui-skin-sam .yui-dt-list tr.yui-dt-selected td,
1039 .yui-skin-sam .yui-dt-list tr.yui-dt-selected td,
1032 .yui-skin-sam .yui-dt-list tr.yui-dt-selected td.yui-dt-asc,
1040 .yui-skin-sam .yui-dt-list tr.yui-dt-selected td.yui-dt-asc,
1033 .yui-skin-sam .yui-dt-list tr.yui-dt-selected td.yui-dt-desc {
1041 .yui-skin-sam .yui-dt-list tr.yui-dt-selected td.yui-dt-desc {
1034 background-color: #426fd9;
1042 background-color: #426fd9;
1035 color: #FFF;
1043 color: #FFF;
1036 }
1044 }
1037 .yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-selected,
1045 .yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-selected,
1038 .yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-selected {
1046 .yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-selected {
1039 background-color: #446cd7;
1047 background-color: #446cd7;
1040 color: #FFF;
1048 color: #FFF;
1041 }
1049 }
1042 .yui-skin-sam .yui-dt-paginator {
1050 .yui-skin-sam .yui-dt-paginator {
1043 display: block;
1051 display: block;
1044 margin: 6px 0;
1052 margin: 6px 0;
1045 white-space: nowrap;
1053 white-space: nowrap;
1046 }
1054 }
1047 .yui-skin-sam .yui-dt-paginator .yui-dt-first,
1055 .yui-skin-sam .yui-dt-paginator .yui-dt-first,
1048 .yui-skin-sam .yui-dt-paginator .yui-dt-last,
1056 .yui-skin-sam .yui-dt-paginator .yui-dt-last,
1049 .yui-skin-sam .yui-dt-paginator .yui-dt-selected { padding: 2px 6px }
1057 .yui-skin-sam .yui-dt-paginator .yui-dt-selected { padding: 2px 6px }
1050 .yui-skin-sam .yui-dt-paginator a.yui-dt-first,
1058 .yui-skin-sam .yui-dt-paginator a.yui-dt-first,
1051 .yui-skin-sam .yui-dt-paginator a.yui-dt-last { text-decoration: none }
1059 .yui-skin-sam .yui-dt-paginator a.yui-dt-last { text-decoration: none }
1052 .yui-skin-sam .yui-dt-paginator .yui-dt-previous,
1060 .yui-skin-sam .yui-dt-paginator .yui-dt-previous,
1053 .yui-skin-sam .yui-dt-paginator .yui-dt-next { display: none }
1061 .yui-skin-sam .yui-dt-paginator .yui-dt-next { display: none }
1054 .yui-skin-sam a.yui-dt-page {
1062 .yui-skin-sam a.yui-dt-page {
1055 border: 1px solid #cbcbcb;
1063 border: 1px solid #cbcbcb;
1056 padding: 2px 6px;
1064 padding: 2px 6px;
1057 text-decoration: none;
1065 text-decoration: none;
1058 background-color: #fff;
1066 background-color: #fff;
1059 }
1067 }
1060 .yui-skin-sam .yui-dt-selected {
1068 .yui-skin-sam .yui-dt-selected {
1061 border: 1px solid #fff;
1069 border: 1px solid #fff;
1062 background-color: #fff;
1070 background-color: #fff;
1063 }
1071 }
1064
1072
1065 #content #left {
1073 #content #left {
1066 left: 0;
1074 left: 0;
1067 width: 280px;
1075 width: 280px;
1068 position: absolute;
1076 position: absolute;
1069 }
1077 }
1070
1078
1071 #content #right {
1079 #content #right {
1072 margin: 0 60px 10px 290px;
1080 margin: 0 60px 10px 290px;
1073 }
1081 }
1074
1082
1075 #content div.box {
1083 #content div.box {
1076 clear: both;
1084 clear: both;
1077 overflow: hidden;
1085 overflow: hidden;
1078 background: #fff;
1086 background: #fff;
1079 margin: 0 0 10px;
1087 margin: 0 0 10px;
1080 padding: 0 0 10px;
1088 padding: 0 0 10px;
1081 -webkit-border-radius: 4px 4px 4px 4px;
1089 -webkit-border-radius: 4px 4px 4px 4px;
1082 -khtml-border-radius: 4px 4px 4px 4px;
1090 -khtml-border-radius: 4px 4px 4px 4px;
1083 -moz-border-radius: 4px 4px 4px 4px;
1091 -moz-border-radius: 4px 4px 4px 4px;
1084 border-radius: 4px 4px 4px 4px;
1092 border-radius: 4px 4px 4px 4px;
1085 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
1093 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
1086 }
1094 }
1087
1095
1088 #content div.box-left {
1096 #content div.box-left {
1089 width: 49%;
1097 width: 49%;
1090 clear: none;
1098 clear: none;
1091 float: left;
1099 float: left;
1092 margin: 0 0 10px;
1100 margin: 0 0 10px;
1093 }
1101 }
1094
1102
1095 #content div.box-right {
1103 #content div.box-right {
1096 width: 49%;
1104 width: 49%;
1097 clear: none;
1105 clear: none;
1098 float: right;
1106 float: right;
1099 margin: 0 0 10px;
1107 margin: 0 0 10px;
1100 }
1108 }
1101
1109
1102 #content div.box div.title {
1110 #content div.box div.title {
1103 clear: both;
1111 clear: both;
1104 overflow: hidden;
1112 overflow: hidden;
1105 background-color: #003B76;
1113 background-color: #003B76;
1106 background-repeat: repeat-x;
1114 background-repeat: repeat-x;
1107 background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
1115 background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
1108 background-image: -moz-linear-gradient(top, #003b76, #00376e);
1116 background-image: -moz-linear-gradient(top, #003b76, #00376e);
1109 background-image: -ms-linear-gradient(top, #003b76, #00376e);
1117 background-image: -ms-linear-gradient(top, #003b76, #00376e);
1110 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76), color-stop(100%, #00376e) );
1118 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76), color-stop(100%, #00376e) );
1111 background-image: -webkit-linear-gradient(top, #003b76, #00376e);
1119 background-image: -webkit-linear-gradient(top, #003b76, #00376e);
1112 background-image: -o-linear-gradient(top, #003b76, #00376e);
1120 background-image: -o-linear-gradient(top, #003b76, #00376e);
1113 background-image: linear-gradient(top, #003b76, #00376e);
1121 background-image: linear-gradient(top, #003b76, #00376e);
1114 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76', endColorstr='#00376e', GradientType=0 );
1122 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76', endColorstr='#00376e', GradientType=0 );
1115 margin: 0 0 20px;
1123 margin: 0 0 20px;
1116 padding: 0;
1124 padding: 0;
1117 }
1125 }
1118
1126
1119 #content div.box div.title h5 {
1127 #content div.box div.title h5 {
1120 float: left;
1128 float: left;
1121 border: none;
1129 border: none;
1122 color: #fff;
1130 color: #fff;
1123 text-transform: uppercase;
1131 text-transform: uppercase;
1124 margin: 0;
1132 margin: 0;
1125 padding: 11px 0 11px 10px;
1133 padding: 11px 0 11px 10px;
1126 }
1134 }
1127
1135
1128 #content div.box div.title .link-white{
1136 #content div.box div.title .link-white{
1129 color: #FFFFFF;
1137 color: #FFFFFF;
1130 }
1138 }
1131
1139
1132 #content div.box div.title .link-white.current{
1140 #content div.box div.title .link-white.current{
1133 color: #BFE3FF;
1141 color: #BFE3FF;
1134 }
1142 }
1135
1143
1136 #content div.box div.title ul.links li {
1144 #content div.box div.title ul.links li {
1137 list-style: none;
1145 list-style: none;
1138 float: left;
1146 float: left;
1139 margin: 0;
1147 margin: 0;
1140 padding: 0;
1148 padding: 0;
1141 }
1149 }
1142
1150
1143 #content div.box div.title ul.links li a {
1151 #content div.box div.title ul.links li a {
1144 border-left: 1px solid #316293;
1152 border-left: 1px solid #316293;
1145 color: #FFFFFF;
1153 color: #FFFFFF;
1146 display: block;
1154 display: block;
1147 float: left;
1155 float: left;
1148 font-size: 13px;
1156 font-size: 13px;
1149 font-weight: 700;
1157 font-weight: 700;
1150 height: 1%;
1158 height: 1%;
1151 margin: 0;
1159 margin: 0;
1152 padding: 11px 22px 12px;
1160 padding: 11px 22px 12px;
1153 text-decoration: none;
1161 text-decoration: none;
1154 }
1162 }
1155
1163
1156 #content div.box h1,#content div.box h2,#content div.box h3,#content div.box h4,#content div.box h5,#content div.box h6,
1164 #content div.box h1,#content div.box h2,#content div.box h3,#content div.box h4,#content div.box h5,#content div.box h6,
1157 #content div.box div.h1,#content div.box div.h2,#content div.box div.h3,#content div.box div.h4,#content div.box div.h5,#content div.box div.h6
1165 #content div.box div.h1,#content div.box div.h2,#content div.box div.h3,#content div.box div.h4,#content div.box div.h5,#content div.box div.h6
1158
1166
1159 {
1167 {
1160 clear: both;
1168 clear: both;
1161 overflow: hidden;
1169 overflow: hidden;
1162 border-bottom: 1px solid #DDD;
1170 border-bottom: 1px solid #DDD;
1163 margin: 10px 20px;
1171 margin: 10px 20px;
1164 padding: 0 0 15px;
1172 padding: 0 0 15px;
1165 }
1173 }
1166
1174
1167 #content div.box p {
1175 #content div.box p {
1168 color: #5f5f5f;
1176 color: #5f5f5f;
1169 font-size: 12px;
1177 font-size: 12px;
1170 line-height: 150%;
1178 line-height: 150%;
1171 margin: 0 24px 10px;
1179 margin: 0 24px 10px;
1172 padding: 0;
1180 padding: 0;
1173 }
1181 }
1174
1182
1175 #content div.box blockquote {
1183 #content div.box blockquote {
1176 border-left: 4px solid #DDD;
1184 border-left: 4px solid #DDD;
1177 color: #5f5f5f;
1185 color: #5f5f5f;
1178 font-size: 11px;
1186 font-size: 11px;
1179 line-height: 150%;
1187 line-height: 150%;
1180 margin: 0 34px;
1188 margin: 0 34px;
1181 padding: 0 0 0 14px;
1189 padding: 0 0 0 14px;
1182 }
1190 }
1183
1191
1184 #content div.box blockquote p {
1192 #content div.box blockquote p {
1185 margin: 10px 0;
1193 margin: 10px 0;
1186 padding: 0;
1194 padding: 0;
1187 }
1195 }
1188
1196
1189 #content div.box dl {
1197 #content div.box dl {
1190 margin: 10px 0px;
1198 margin: 10px 0px;
1191 }
1199 }
1192
1200
1193 #content div.box dt {
1201 #content div.box dt {
1194 font-size: 12px;
1202 font-size: 12px;
1195 margin: 0;
1203 margin: 0;
1196 }
1204 }
1197
1205
1198 #content div.box dd {
1206 #content div.box dd {
1199 font-size: 12px;
1207 font-size: 12px;
1200 margin: 0;
1208 margin: 0;
1201 padding: 8px 0 8px 15px;
1209 padding: 8px 0 8px 15px;
1202 }
1210 }
1203
1211
1204 #content div.box li {
1212 #content div.box li {
1205 font-size: 12px;
1213 font-size: 12px;
1206 padding: 4px 0;
1214 padding: 4px 0;
1207 }
1215 }
1208
1216
1209 #content div.box ul.disc,#content div.box ul.circle {
1217 #content div.box ul.disc,#content div.box ul.circle {
1210 margin: 10px 24px 10px 38px;
1218 margin: 10px 24px 10px 38px;
1211 }
1219 }
1212
1220
1213 #content div.box ul.square {
1221 #content div.box ul.square {
1214 margin: 10px 24px 10px 40px;
1222 margin: 10px 24px 10px 40px;
1215 }
1223 }
1216
1224
1217 #content div.box img.left {
1225 #content div.box img.left {
1218 border: none;
1226 border: none;
1219 float: left;
1227 float: left;
1220 margin: 10px 10px 10px 0;
1228 margin: 10px 10px 10px 0;
1221 }
1229 }
1222
1230
1223 #content div.box img.right {
1231 #content div.box img.right {
1224 border: none;
1232 border: none;
1225 float: right;
1233 float: right;
1226 margin: 10px 0 10px 10px;
1234 margin: 10px 0 10px 10px;
1227 }
1235 }
1228
1236
1229 #content div.box div.messages {
1237 #content div.box div.messages {
1230 clear: both;
1238 clear: both;
1231 overflow: hidden;
1239 overflow: hidden;
1232 margin: 0 20px;
1240 margin: 0 20px;
1233 padding: 0;
1241 padding: 0;
1234 }
1242 }
1235
1243
1236 #content div.box div.message {
1244 #content div.box div.message {
1237 clear: both;
1245 clear: both;
1238 overflow: hidden;
1246 overflow: hidden;
1239 margin: 0;
1247 margin: 0;
1240 padding: 5px 0;
1248 padding: 5px 0;
1241 white-space: pre-wrap;
1249 white-space: pre-wrap;
1242 }
1250 }
1243 #content div.box div.expand {
1251 #content div.box div.expand {
1244 width: 110%;
1252 width: 110%;
1245 height:14px;
1253 height:14px;
1246 font-size:10px;
1254 font-size:10px;
1247 text-align:center;
1255 text-align:center;
1248 cursor: pointer;
1256 cursor: pointer;
1249 color:#666;
1257 color:#666;
1250
1258
1251 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)));
1259 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)));
1252 background:-webkit-linear-gradient(top,rgba(255,255,255,0),rgba(64,96,128,0.1));
1260 background:-webkit-linear-gradient(top,rgba(255,255,255,0),rgba(64,96,128,0.1));
1253 background:-moz-linear-gradient(top,rgba(255,255,255,0),rgba(64,96,128,0.1));
1261 background:-moz-linear-gradient(top,rgba(255,255,255,0),rgba(64,96,128,0.1));
1254 background:-o-linear-gradient(top,rgba(255,255,255,0),rgba(64,96,128,0.1));
1262 background:-o-linear-gradient(top,rgba(255,255,255,0),rgba(64,96,128,0.1));
1255 background:-ms-linear-gradient(top,rgba(255,255,255,0),rgba(64,96,128,0.1));
1263 background:-ms-linear-gradient(top,rgba(255,255,255,0),rgba(64,96,128,0.1));
1256 background:linear-gradient(top,rgba(255,255,255,0),rgba(64,96,128,0.1));
1264 background:linear-gradient(top,rgba(255,255,255,0),rgba(64,96,128,0.1));
1257
1265
1258 display: none;
1266 display: none;
1259 }
1267 }
1260 #content div.box div.expand .expandtext {
1268 #content div.box div.expand .expandtext {
1261 background-color: #ffffff;
1269 background-color: #ffffff;
1262 padding: 2px;
1270 padding: 2px;
1263 border-radius: 2px;
1271 border-radius: 2px;
1264 }
1272 }
1265
1273
1266 #content div.box div.message a {
1274 #content div.box div.message a {
1267 font-weight: 400 !important;
1275 font-weight: 400 !important;
1268 }
1276 }
1269
1277
1270 #content div.box div.message div.image {
1278 #content div.box div.message div.image {
1271 float: left;
1279 float: left;
1272 margin: 9px 0 0 5px;
1280 margin: 9px 0 0 5px;
1273 padding: 6px;
1281 padding: 6px;
1274 }
1282 }
1275
1283
1276 #content div.box div.message div.image img {
1284 #content div.box div.message div.image img {
1277 vertical-align: middle;
1285 vertical-align: middle;
1278 margin: 0;
1286 margin: 0;
1279 }
1287 }
1280
1288
1281 #content div.box div.message div.text {
1289 #content div.box div.message div.text {
1282 float: left;
1290 float: left;
1283 margin: 0;
1291 margin: 0;
1284 padding: 9px 6px;
1292 padding: 9px 6px;
1285 }
1293 }
1286
1294
1287 #content div.box div.message div.dismiss a {
1295 #content div.box div.message div.dismiss a {
1288 height: 16px;
1296 height: 16px;
1289 width: 16px;
1297 width: 16px;
1290 display: block;
1298 display: block;
1291 background: url("../images/icons/cross.png") no-repeat;
1299 background: url("../images/icons/cross.png") no-repeat;
1292 margin: 15px 14px 0 0;
1300 margin: 15px 14px 0 0;
1293 padding: 0;
1301 padding: 0;
1294 }
1302 }
1295
1303
1296 #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
1304 #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
1297 {
1305 {
1298 border: none;
1306 border: none;
1299 margin: 0;
1307 margin: 0;
1300 padding: 0;
1308 padding: 0;
1301 }
1309 }
1302
1310
1303 #content div.box div.message div.text span {
1311 #content div.box div.message div.text span {
1304 height: 1%;
1312 height: 1%;
1305 display: block;
1313 display: block;
1306 margin: 0;
1314 margin: 0;
1307 padding: 5px 0 0;
1315 padding: 5px 0 0;
1308 }
1316 }
1309
1317
1310 #content div.box div.message-error {
1318 #content div.box div.message-error {
1311 height: 1%;
1319 height: 1%;
1312 clear: both;
1320 clear: both;
1313 overflow: hidden;
1321 overflow: hidden;
1314 background: #FBE3E4;
1322 background: #FBE3E4;
1315 border: 1px solid #FBC2C4;
1323 border: 1px solid #FBC2C4;
1316 color: #860006;
1324 color: #860006;
1317 }
1325 }
1318
1326
1319 #content div.box div.message-error h6 {
1327 #content div.box div.message-error h6 {
1320 color: #860006;
1328 color: #860006;
1321 }
1329 }
1322
1330
1323 #content div.box div.message-warning {
1331 #content div.box div.message-warning {
1324 height: 1%;
1332 height: 1%;
1325 clear: both;
1333 clear: both;
1326 overflow: hidden;
1334 overflow: hidden;
1327 background: #FFF6BF;
1335 background: #FFF6BF;
1328 border: 1px solid #FFD324;
1336 border: 1px solid #FFD324;
1329 color: #5f5200;
1337 color: #5f5200;
1330 }
1338 }
1331
1339
1332 #content div.box div.message-warning h6 {
1340 #content div.box div.message-warning h6 {
1333 color: #5f5200;
1341 color: #5f5200;
1334 }
1342 }
1335
1343
1336 #content div.box div.message-notice {
1344 #content div.box div.message-notice {
1337 height: 1%;
1345 height: 1%;
1338 clear: both;
1346 clear: both;
1339 overflow: hidden;
1347 overflow: hidden;
1340 background: #8FBDE0;
1348 background: #8FBDE0;
1341 border: 1px solid #6BACDE;
1349 border: 1px solid #6BACDE;
1342 color: #003863;
1350 color: #003863;
1343 }
1351 }
1344
1352
1345 #content div.box div.message-notice h6 {
1353 #content div.box div.message-notice h6 {
1346 color: #003863;
1354 color: #003863;
1347 }
1355 }
1348
1356
1349 #content div.box div.message-success {
1357 #content div.box div.message-success {
1350 height: 1%;
1358 height: 1%;
1351 clear: both;
1359 clear: both;
1352 overflow: hidden;
1360 overflow: hidden;
1353 background: #E6EFC2;
1361 background: #E6EFC2;
1354 border: 1px solid #C6D880;
1362 border: 1px solid #C6D880;
1355 color: #4e6100;
1363 color: #4e6100;
1356 }
1364 }
1357
1365
1358 #content div.box div.message-success h6 {
1366 #content div.box div.message-success h6 {
1359 color: #4e6100;
1367 color: #4e6100;
1360 }
1368 }
1361
1369
1362 #content div.box div.form div.fields div.field {
1370 #content div.box div.form div.fields div.field {
1363 height: 1%;
1371 height: 1%;
1364 border-bottom: 1px solid #DDD;
1372 border-bottom: 1px solid #DDD;
1365 clear: both;
1373 clear: both;
1366 margin: 0;
1374 margin: 0;
1367 padding: 10px 0;
1375 padding: 10px 0;
1368 }
1376 }
1369
1377
1370 #content div.box div.form div.fields div.field-first {
1378 #content div.box div.form div.fields div.field-first {
1371 padding: 0 0 10px;
1379 padding: 0 0 10px;
1372 }
1380 }
1373
1381
1374 #content div.box div.form div.fields div.field-noborder {
1382 #content div.box div.form div.fields div.field-noborder {
1375 border-bottom: 0 !important;
1383 border-bottom: 0 !important;
1376 }
1384 }
1377
1385
1378 #content div.box div.form div.fields div.field span.error-message {
1386 #content div.box div.form div.fields div.field span.error-message {
1379 height: 1%;
1387 height: 1%;
1380 display: inline-block;
1388 display: inline-block;
1381 color: red;
1389 color: red;
1382 margin: 8px 0 0 4px;
1390 margin: 8px 0 0 4px;
1383 padding: 0;
1391 padding: 0;
1384 }
1392 }
1385
1393
1386 #content div.box div.form div.fields div.field span.success {
1394 #content div.box div.form div.fields div.field span.success {
1387 height: 1%;
1395 height: 1%;
1388 display: block;
1396 display: block;
1389 color: #316309;
1397 color: #316309;
1390 margin: 8px 0 0;
1398 margin: 8px 0 0;
1391 padding: 0;
1399 padding: 0;
1392 }
1400 }
1393
1401
1394 #content div.box div.form div.fields div.field div.label {
1402 #content div.box div.form div.fields div.field div.label {
1395 left: 70px;
1403 left: 70px;
1396 width: 155px;
1404 width: 155px;
1397 position: absolute;
1405 position: absolute;
1398 margin: 0;
1406 margin: 0;
1399 padding: 5px 0 0 0px;
1407 padding: 5px 0 0 0px;
1400 }
1408 }
1401
1409
1402 #content div.box div.form div.fields div.field div.label-summary {
1410 #content div.box div.form div.fields div.field div.label-summary {
1403 left: 30px;
1411 left: 30px;
1404 width: 155px;
1412 width: 155px;
1405 position: absolute;
1413 position: absolute;
1406 margin: 0;
1414 margin: 0;
1407 padding: 0px 0 0 0px;
1415 padding: 0px 0 0 0px;
1408 }
1416 }
1409
1417
1410 #content div.box-left div.form div.fields div.field div.label,
1418 #content div.box-left div.form div.fields div.field div.label,
1411 #content div.box-right div.form div.fields div.field div.label,
1419 #content div.box-right div.form div.fields div.field div.label,
1412 #content div.box-left div.form div.fields div.field div.label,
1420 #content div.box-left div.form div.fields div.field div.label,
1413 #content div.box-left div.form div.fields div.field div.label-summary,
1421 #content div.box-left div.form div.fields div.field div.label-summary,
1414 #content div.box-right div.form div.fields div.field div.label-summary,
1422 #content div.box-right div.form div.fields div.field div.label-summary,
1415 #content div.box-left div.form div.fields div.field div.label-summary
1423 #content div.box-left div.form div.fields div.field div.label-summary
1416 {
1424 {
1417 clear: both;
1425 clear: both;
1418 overflow: hidden;
1426 overflow: hidden;
1419 left: 0;
1427 left: 0;
1420 width: auto;
1428 width: auto;
1421 position: relative;
1429 position: relative;
1422 margin: 0;
1430 margin: 0;
1423 padding: 0 0 8px;
1431 padding: 0 0 8px;
1424 }
1432 }
1425
1433
1426 #content div.box div.form div.fields div.field div.label-select {
1434 #content div.box div.form div.fields div.field div.label-select {
1427 padding: 5px 0 0 5px;
1435 padding: 5px 0 0 5px;
1428 }
1436 }
1429
1437
1430 #content div.box-left div.form div.fields div.field div.label-select,
1438 #content div.box-left div.form div.fields div.field div.label-select,
1431 #content div.box-right div.form div.fields div.field div.label-select
1439 #content div.box-right div.form div.fields div.field div.label-select
1432 {
1440 {
1433 padding: 0 0 8px;
1441 padding: 0 0 8px;
1434 }
1442 }
1435
1443
1436 #content div.box-left div.form div.fields div.field div.label-textarea,
1444 #content div.box-left div.form div.fields div.field div.label-textarea,
1437 #content div.box-right div.form div.fields div.field div.label-textarea
1445 #content div.box-right div.form div.fields div.field div.label-textarea
1438 {
1446 {
1439 padding: 0 0 8px !important;
1447 padding: 0 0 8px !important;
1440 }
1448 }
1441
1449
1442 #content div.box div.form div.fields div.field div.label label,div.label label
1450 #content div.box div.form div.fields div.field div.label label,div.label label
1443 {
1451 {
1444 color: #393939;
1452 color: #393939;
1445 font-weight: 700;
1453 font-weight: 700;
1446 }
1454 }
1447 #content div.box div.form div.fields div.field div.label label,div.label-summary label
1455 #content div.box div.form div.fields div.field div.label label,div.label-summary label
1448 {
1456 {
1449 color: #393939;
1457 color: #393939;
1450 font-weight: 700;
1458 font-weight: 700;
1451 }
1459 }
1452 #content div.box div.form div.fields div.field div.input {
1460 #content div.box div.form div.fields div.field div.input {
1453 margin: 0 0 0 200px;
1461 margin: 0 0 0 200px;
1454 }
1462 }
1455
1463
1456 #content div.box div.form div.fields div.field div.input.summary {
1464 #content div.box div.form div.fields div.field div.input.summary {
1457 margin: 0 0 0 110px;
1465 margin: 0 0 0 110px;
1458 }
1466 }
1459 #content div.box div.form div.fields div.field div.input.summary-short {
1467 #content div.box div.form div.fields div.field div.input.summary-short {
1460 margin: 0 0 0 110px;
1468 margin: 0 0 0 110px;
1461 }
1469 }
1462 #content div.box div.form div.fields div.field div.file {
1470 #content div.box div.form div.fields div.field div.file {
1463 margin: 0 0 0 200px;
1471 margin: 0 0 0 200px;
1464 }
1472 }
1465
1473
1466 #content div.box-left div.form div.fields div.field div.input,#content div.box-right div.form div.fields div.field div.input
1474 #content div.box-left div.form div.fields div.field div.input,#content div.box-right div.form div.fields div.field div.input
1467 {
1475 {
1468 margin: 0 0 0 0px;
1476 margin: 0 0 0 0px;
1469 }
1477 }
1470
1478
1471 #content div.box div.form div.fields div.field div.input input,
1479 #content div.box div.form div.fields div.field div.input input,
1472 .reviewer_ac input {
1480 .reviewer_ac input {
1473 background: #FFF;
1481 background: #FFF;
1474 border-top: 1px solid #b3b3b3;
1482 border-top: 1px solid #b3b3b3;
1475 border-left: 1px solid #b3b3b3;
1483 border-left: 1px solid #b3b3b3;
1476 border-right: 1px solid #eaeaea;
1484 border-right: 1px solid #eaeaea;
1477 border-bottom: 1px solid #eaeaea;
1485 border-bottom: 1px solid #eaeaea;
1478 color: #000;
1486 color: #000;
1479 font-size: 11px;
1487 font-size: 11px;
1480 margin: 0;
1488 margin: 0;
1481 padding: 7px 7px 6px;
1489 padding: 7px 7px 6px;
1482 }
1490 }
1483
1491
1484 #content div.box div.form div.fields div.field div.input input#clone_url,
1492 #content div.box div.form div.fields div.field div.input input#clone_url,
1485 #content div.box div.form div.fields div.field div.input input#clone_url_id
1493 #content div.box div.form div.fields div.field div.input input#clone_url_id
1486 {
1494 {
1487 font-size: 16px;
1495 font-size: 16px;
1488 padding: 2px;
1496 padding: 2px;
1489 }
1497 }
1490
1498
1491 #content div.box div.form div.fields div.field div.file input {
1499 #content div.box div.form div.fields div.field div.file input {
1492 background: none repeat scroll 0 0 #FFFFFF;
1500 background: none repeat scroll 0 0 #FFFFFF;
1493 border-color: #B3B3B3 #EAEAEA #EAEAEA #B3B3B3;
1501 border-color: #B3B3B3 #EAEAEA #EAEAEA #B3B3B3;
1494 border-style: solid;
1502 border-style: solid;
1495 border-width: 1px;
1503 border-width: 1px;
1496 color: #000000;
1504 color: #000000;
1497 font-size: 11px;
1505 font-size: 11px;
1498 margin: 0;
1506 margin: 0;
1499 padding: 7px 7px 6px;
1507 padding: 7px 7px 6px;
1500 }
1508 }
1501
1509
1502 input.disabled {
1510 input.disabled {
1503 background-color: #F5F5F5 !important;
1511 background-color: #F5F5F5 !important;
1504 }
1512 }
1505 #content div.box div.form div.fields div.field div.input input.small {
1513 #content div.box div.form div.fields div.field div.input input.small {
1506 width: 30%;
1514 width: 30%;
1507 }
1515 }
1508
1516
1509 #content div.box div.form div.fields div.field div.input input.medium {
1517 #content div.box div.form div.fields div.field div.input input.medium {
1510 width: 55%;
1518 width: 55%;
1511 }
1519 }
1512
1520
1513 #content div.box div.form div.fields div.field div.input input.large {
1521 #content div.box div.form div.fields div.field div.input input.large {
1514 width: 85%;
1522 width: 85%;
1515 }
1523 }
1516
1524
1517 #content div.box div.form div.fields div.field div.input input.date {
1525 #content div.box div.form div.fields div.field div.input input.date {
1518 width: 177px;
1526 width: 177px;
1519 }
1527 }
1520
1528
1521 #content div.box div.form div.fields div.field div.input input.button {
1529 #content div.box div.form div.fields div.field div.input input.button {
1522 background: #D4D0C8;
1530 background: #D4D0C8;
1523 border-top: 1px solid #FFF;
1531 border-top: 1px solid #FFF;
1524 border-left: 1px solid #FFF;
1532 border-left: 1px solid #FFF;
1525 border-right: 1px solid #404040;
1533 border-right: 1px solid #404040;
1526 border-bottom: 1px solid #404040;
1534 border-bottom: 1px solid #404040;
1527 color: #000;
1535 color: #000;
1528 margin: 0;
1536 margin: 0;
1529 padding: 4px 8px;
1537 padding: 4px 8px;
1530 }
1538 }
1531
1539
1532 #content div.box div.form div.fields div.field div.textarea {
1540 #content div.box div.form div.fields div.field div.textarea {
1533 border-top: 1px solid #b3b3b3;
1541 border-top: 1px solid #b3b3b3;
1534 border-left: 1px solid #b3b3b3;
1542 border-left: 1px solid #b3b3b3;
1535 border-right: 1px solid #eaeaea;
1543 border-right: 1px solid #eaeaea;
1536 border-bottom: 1px solid #eaeaea;
1544 border-bottom: 1px solid #eaeaea;
1537 margin: 0 0 0 200px;
1545 margin: 0 0 0 200px;
1538 padding: 10px;
1546 padding: 10px;
1539 }
1547 }
1540
1548
1541 #content div.box div.form div.fields div.field div.textarea-editor {
1549 #content div.box div.form div.fields div.field div.textarea-editor {
1542 border: 1px solid #ddd;
1550 border: 1px solid #ddd;
1543 padding: 0;
1551 padding: 0;
1544 }
1552 }
1545
1553
1546 #content div.box div.form div.fields div.field div.textarea textarea {
1554 #content div.box div.form div.fields div.field div.textarea textarea {
1547 width: 100%;
1555 width: 100%;
1548 height: 220px;
1556 height: 220px;
1549 overflow: hidden;
1557 overflow: hidden;
1550 background: #FFF;
1558 background: #FFF;
1551 color: #000;
1559 color: #000;
1552 font-size: 11px;
1560 font-size: 11px;
1553 outline: none;
1561 outline: none;
1554 border-width: 0;
1562 border-width: 0;
1555 margin: 0;
1563 margin: 0;
1556 padding: 0;
1564 padding: 0;
1557 }
1565 }
1558
1566
1559 #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
1567 #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
1560 {
1568 {
1561 width: 100%;
1569 width: 100%;
1562 height: 100px;
1570 height: 100px;
1563 }
1571 }
1564
1572
1565 #content div.box div.form div.fields div.field div.textarea table {
1573 #content div.box div.form div.fields div.field div.textarea table {
1566 width: 100%;
1574 width: 100%;
1567 border: none;
1575 border: none;
1568 margin: 0;
1576 margin: 0;
1569 padding: 0;
1577 padding: 0;
1570 }
1578 }
1571
1579
1572 #content div.box div.form div.fields div.field div.textarea table td {
1580 #content div.box div.form div.fields div.field div.textarea table td {
1573 background: #DDD;
1581 background: #DDD;
1574 border: none;
1582 border: none;
1575 padding: 0;
1583 padding: 0;
1576 }
1584 }
1577
1585
1578 #content div.box div.form div.fields div.field div.textarea table td table
1586 #content div.box div.form div.fields div.field div.textarea table td table
1579 {
1587 {
1580 width: auto;
1588 width: auto;
1581 border: none;
1589 border: none;
1582 margin: 0;
1590 margin: 0;
1583 padding: 0;
1591 padding: 0;
1584 }
1592 }
1585
1593
1586 #content div.box div.form div.fields div.field div.textarea table td table td
1594 #content div.box div.form div.fields div.field div.textarea table td table td
1587 {
1595 {
1588 font-size: 11px;
1596 font-size: 11px;
1589 padding: 5px 5px 5px 0;
1597 padding: 5px 5px 5px 0;
1590 }
1598 }
1591
1599
1592 #content div.box div.form div.fields div.field input[type=text]:focus,
1600 #content div.box div.form div.fields div.field input[type=text]:focus,
1593 #content div.box div.form div.fields div.field input[type=password]:focus,
1601 #content div.box div.form div.fields div.field input[type=password]:focus,
1594 #content div.box div.form div.fields div.field input[type=file]:focus,
1602 #content div.box div.form div.fields div.field input[type=file]:focus,
1595 #content div.box div.form div.fields div.field textarea:focus,
1603 #content div.box div.form div.fields div.field textarea:focus,
1596 #content div.box div.form div.fields div.field select:focus,
1604 #content div.box div.form div.fields div.field select:focus,
1597 .reviewer_ac input:focus
1605 .reviewer_ac input:focus
1598 {
1606 {
1599 background: #f6f6f6;
1607 background: #f6f6f6;
1600 border-color: #666;
1608 border-color: #666;
1601 }
1609 }
1602
1610
1603 .reviewer_ac {
1611 .reviewer_ac {
1604 padding:10px
1612 padding:10px
1605 }
1613 }
1606
1614
1607 div.form div.fields div.field div.button {
1615 div.form div.fields div.field div.button {
1608 margin: 0;
1616 margin: 0;
1609 padding: 0 0 0 8px;
1617 padding: 0 0 0 8px;
1610 }
1618 }
1611 #content div.box table.noborder {
1619 #content div.box table.noborder {
1612 border: 1px solid transparent;
1620 border: 1px solid transparent;
1613 }
1621 }
1614
1622
1615 #content div.box table {
1623 #content div.box table {
1616 width: 100%;
1624 width: 100%;
1617 border-collapse: separate;
1625 border-collapse: separate;
1618 margin: 0;
1626 margin: 0;
1619 padding: 0;
1627 padding: 0;
1620 border: 1px solid #eee;
1628 border: 1px solid #eee;
1621 -webkit-border-radius: 4px;
1629 -webkit-border-radius: 4px;
1622 -moz-border-radius: 4px;
1630 -moz-border-radius: 4px;
1623 border-radius: 4px;
1631 border-radius: 4px;
1624 }
1632 }
1625
1633
1626 #content div.box table th {
1634 #content div.box table th {
1627 background: #eee;
1635 background: #eee;
1628 border-bottom: 1px solid #ddd;
1636 border-bottom: 1px solid #ddd;
1629 padding: 5px 0px 5px 5px;
1637 padding: 5px 0px 5px 5px;
1630 text-align: left;
1638 text-align: left;
1631 }
1639 }
1632
1640
1633 #content div.box table th.left {
1641 #content div.box table th.left {
1634 text-align: left;
1642 text-align: left;
1635 }
1643 }
1636
1644
1637 #content div.box table th.right {
1645 #content div.box table th.right {
1638 text-align: right;
1646 text-align: right;
1639 }
1647 }
1640
1648
1641 #content div.box table th.center {
1649 #content div.box table th.center {
1642 text-align: center;
1650 text-align: center;
1643 }
1651 }
1644
1652
1645 #content div.box table th.selected {
1653 #content div.box table th.selected {
1646 vertical-align: middle;
1654 vertical-align: middle;
1647 padding: 0;
1655 padding: 0;
1648 }
1656 }
1649
1657
1650 #content div.box table td {
1658 #content div.box table td {
1651 background: #fff;
1659 background: #fff;
1652 border-bottom: 1px solid #cdcdcd;
1660 border-bottom: 1px solid #cdcdcd;
1653 vertical-align: middle;
1661 vertical-align: middle;
1654 padding: 5px;
1662 padding: 5px;
1655 }
1663 }
1656
1664
1657 #content div.box table tr.selected td {
1665 #content div.box table tr.selected td {
1658 background: #FFC;
1666 background: #FFC;
1659 }
1667 }
1660
1668
1661 #content div.box table td.selected {
1669 #content div.box table td.selected {
1662 width: 3%;
1670 width: 3%;
1663 text-align: center;
1671 text-align: center;
1664 vertical-align: middle;
1672 vertical-align: middle;
1665 padding: 0;
1673 padding: 0;
1666 }
1674 }
1667
1675
1668 #content div.box table td.action {
1676 #content div.box table td.action {
1669 width: 45%;
1677 width: 45%;
1670 text-align: left;
1678 text-align: left;
1671 }
1679 }
1672
1680
1673 #content div.box table td.date {
1681 #content div.box table td.date {
1674 width: 33%;
1682 width: 33%;
1675 text-align: center;
1683 text-align: center;
1676 }
1684 }
1677
1685
1678 #content div.box div.action {
1686 #content div.box div.action {
1679 float: right;
1687 float: right;
1680 background: #FFF;
1688 background: #FFF;
1681 text-align: right;
1689 text-align: right;
1682 margin: 10px 0 0;
1690 margin: 10px 0 0;
1683 padding: 0;
1691 padding: 0;
1684 }
1692 }
1685
1693
1686 #content div.box div.action select {
1694 #content div.box div.action select {
1687 font-size: 11px;
1695 font-size: 11px;
1688 margin: 0;
1696 margin: 0;
1689 }
1697 }
1690
1698
1691 #content div.box div.action .ui-selectmenu {
1699 #content div.box div.action .ui-selectmenu {
1692 margin: 0;
1700 margin: 0;
1693 padding: 0;
1701 padding: 0;
1694 }
1702 }
1695
1703
1696 #content div.box div.pagination {
1704 #content div.box div.pagination {
1697 height: 1%;
1705 height: 1%;
1698 clear: both;
1706 clear: both;
1699 overflow: hidden;
1707 overflow: hidden;
1700 margin: 10px 0 0;
1708 margin: 10px 0 0;
1701 padding: 0;
1709 padding: 0;
1702 }
1710 }
1703
1711
1704 #content div.box div.pagination ul.pager {
1712 #content div.box div.pagination ul.pager {
1705 float: right;
1713 float: right;
1706 text-align: right;
1714 text-align: right;
1707 margin: 0;
1715 margin: 0;
1708 padding: 0;
1716 padding: 0;
1709 }
1717 }
1710
1718
1711 #content div.box div.pagination ul.pager li {
1719 #content div.box div.pagination ul.pager li {
1712 height: 1%;
1720 height: 1%;
1713 float: left;
1721 float: left;
1714 list-style: none;
1722 list-style: none;
1715 background: #ebebeb url("../images/pager.png") repeat-x;
1723 background: #ebebeb url("../images/pager.png") repeat-x;
1716 border-top: 1px solid #dedede;
1724 border-top: 1px solid #dedede;
1717 border-left: 1px solid #cfcfcf;
1725 border-left: 1px solid #cfcfcf;
1718 border-right: 1px solid #c4c4c4;
1726 border-right: 1px solid #c4c4c4;
1719 border-bottom: 1px solid #c4c4c4;
1727 border-bottom: 1px solid #c4c4c4;
1720 color: #4A4A4A;
1728 color: #4A4A4A;
1721 font-weight: 700;
1729 font-weight: 700;
1722 margin: 0 0 0 4px;
1730 margin: 0 0 0 4px;
1723 padding: 0;
1731 padding: 0;
1724 }
1732 }
1725
1733
1726 #content div.box div.pagination ul.pager li.separator {
1734 #content div.box div.pagination ul.pager li.separator {
1727 padding: 6px;
1735 padding: 6px;
1728 }
1736 }
1729
1737
1730 #content div.box div.pagination ul.pager li.current {
1738 #content div.box div.pagination ul.pager li.current {
1731 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1739 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1732 border-top: 1px solid #ccc;
1740 border-top: 1px solid #ccc;
1733 border-left: 1px solid #bebebe;
1741 border-left: 1px solid #bebebe;
1734 border-right: 1px solid #b1b1b1;
1742 border-right: 1px solid #b1b1b1;
1735 border-bottom: 1px solid #afafaf;
1743 border-bottom: 1px solid #afafaf;
1736 color: #515151;
1744 color: #515151;
1737 padding: 6px;
1745 padding: 6px;
1738 }
1746 }
1739
1747
1740 #content div.box div.pagination ul.pager li a {
1748 #content div.box div.pagination ul.pager li a {
1741 height: 1%;
1749 height: 1%;
1742 display: block;
1750 display: block;
1743 float: left;
1751 float: left;
1744 color: #515151;
1752 color: #515151;
1745 text-decoration: none;
1753 text-decoration: none;
1746 margin: 0;
1754 margin: 0;
1747 padding: 6px;
1755 padding: 6px;
1748 }
1756 }
1749
1757
1750 #content div.box div.pagination ul.pager li a:hover,#content div.box div.pagination ul.pager li a:active
1758 #content div.box div.pagination ul.pager li a:hover,#content div.box div.pagination ul.pager li a:active
1751 {
1759 {
1752 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1760 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1753 border-top: 1px solid #ccc;
1761 border-top: 1px solid #ccc;
1754 border-left: 1px solid #bebebe;
1762 border-left: 1px solid #bebebe;
1755 border-right: 1px solid #b1b1b1;
1763 border-right: 1px solid #b1b1b1;
1756 border-bottom: 1px solid #afafaf;
1764 border-bottom: 1px solid #afafaf;
1757 margin: -1px;
1765 margin: -1px;
1758 }
1766 }
1759
1767
1760 #content div.box div.pagination-wh {
1768 #content div.box div.pagination-wh {
1761 height: 1%;
1769 height: 1%;
1762 clear: both;
1770 clear: both;
1763 overflow: hidden;
1771 overflow: hidden;
1764 text-align: right;
1772 text-align: right;
1765 margin: 10px 0 0;
1773 margin: 10px 0 0;
1766 padding: 0;
1774 padding: 0;
1767 }
1775 }
1768
1776
1769 #content div.box div.pagination-right {
1777 #content div.box div.pagination-right {
1770 float: right;
1778 float: right;
1771 }
1779 }
1772
1780
1773 #content div.box div.pagination-wh a,
1781 #content div.box div.pagination-wh a,
1774 #content div.box div.pagination-wh span.pager_dotdot,
1782 #content div.box div.pagination-wh span.pager_dotdot,
1775 #content div.box div.pagination-wh span.yui-pg-previous,
1783 #content div.box div.pagination-wh span.yui-pg-previous,
1776 #content div.box div.pagination-wh span.yui-pg-last,
1784 #content div.box div.pagination-wh span.yui-pg-last,
1777 #content div.box div.pagination-wh span.yui-pg-next,
1785 #content div.box div.pagination-wh span.yui-pg-next,
1778 #content div.box div.pagination-wh span.yui-pg-first
1786 #content div.box div.pagination-wh span.yui-pg-first
1779 {
1787 {
1780 height: 1%;
1788 height: 1%;
1781 float: left;
1789 float: left;
1782 background: #ebebeb url("../images/pager.png") repeat-x;
1790 background: #ebebeb url("../images/pager.png") repeat-x;
1783 border-top: 1px solid #dedede;
1791 border-top: 1px solid #dedede;
1784 border-left: 1px solid #cfcfcf;
1792 border-left: 1px solid #cfcfcf;
1785 border-right: 1px solid #c4c4c4;
1793 border-right: 1px solid #c4c4c4;
1786 border-bottom: 1px solid #c4c4c4;
1794 border-bottom: 1px solid #c4c4c4;
1787 color: #4A4A4A;
1795 color: #4A4A4A;
1788 font-weight: 700;
1796 font-weight: 700;
1789 margin: 0 0 0 4px;
1797 margin: 0 0 0 4px;
1790 padding: 6px;
1798 padding: 6px;
1791 }
1799 }
1792
1800
1793 #content div.box div.pagination-wh span.pager_curpage {
1801 #content div.box div.pagination-wh span.pager_curpage {
1794 height: 1%;
1802 height: 1%;
1795 float: left;
1803 float: left;
1796 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1804 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1797 border-top: 1px solid #ccc;
1805 border-top: 1px solid #ccc;
1798 border-left: 1px solid #bebebe;
1806 border-left: 1px solid #bebebe;
1799 border-right: 1px solid #b1b1b1;
1807 border-right: 1px solid #b1b1b1;
1800 border-bottom: 1px solid #afafaf;
1808 border-bottom: 1px solid #afafaf;
1801 color: #515151;
1809 color: #515151;
1802 font-weight: 700;
1810 font-weight: 700;
1803 margin: 0 0 0 4px;
1811 margin: 0 0 0 4px;
1804 padding: 6px;
1812 padding: 6px;
1805 }
1813 }
1806
1814
1807 #content div.box div.pagination-wh a:hover,#content div.box div.pagination-wh a:active
1815 #content div.box div.pagination-wh a:hover,#content div.box div.pagination-wh a:active
1808 {
1816 {
1809 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1817 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1810 border-top: 1px solid #ccc;
1818 border-top: 1px solid #ccc;
1811 border-left: 1px solid #bebebe;
1819 border-left: 1px solid #bebebe;
1812 border-right: 1px solid #b1b1b1;
1820 border-right: 1px solid #b1b1b1;
1813 border-bottom: 1px solid #afafaf;
1821 border-bottom: 1px solid #afafaf;
1814 text-decoration: none;
1822 text-decoration: none;
1815 }
1823 }
1816
1824
1817 #content div.box div.traffic div.legend {
1825 #content div.box div.traffic div.legend {
1818 clear: both;
1826 clear: both;
1819 overflow: hidden;
1827 overflow: hidden;
1820 border-bottom: 1px solid #ddd;
1828 border-bottom: 1px solid #ddd;
1821 margin: 0 0 10px;
1829 margin: 0 0 10px;
1822 padding: 0 0 10px;
1830 padding: 0 0 10px;
1823 }
1831 }
1824
1832
1825 #content div.box div.traffic div.legend h6 {
1833 #content div.box div.traffic div.legend h6 {
1826 float: left;
1834 float: left;
1827 border: none;
1835 border: none;
1828 margin: 0;
1836 margin: 0;
1829 padding: 0;
1837 padding: 0;
1830 }
1838 }
1831
1839
1832 #content div.box div.traffic div.legend li {
1840 #content div.box div.traffic div.legend li {
1833 list-style: none;
1841 list-style: none;
1834 float: left;
1842 float: left;
1835 font-size: 11px;
1843 font-size: 11px;
1836 margin: 0;
1844 margin: 0;
1837 padding: 0 8px 0 4px;
1845 padding: 0 8px 0 4px;
1838 }
1846 }
1839
1847
1840 #content div.box div.traffic div.legend li.visits {
1848 #content div.box div.traffic div.legend li.visits {
1841 border-left: 12px solid #edc240;
1849 border-left: 12px solid #edc240;
1842 }
1850 }
1843
1851
1844 #content div.box div.traffic div.legend li.pageviews {
1852 #content div.box div.traffic div.legend li.pageviews {
1845 border-left: 12px solid #afd8f8;
1853 border-left: 12px solid #afd8f8;
1846 }
1854 }
1847
1855
1848 #content div.box div.traffic table {
1856 #content div.box div.traffic table {
1849 width: auto;
1857 width: auto;
1850 }
1858 }
1851
1859
1852 #content div.box div.traffic table td {
1860 #content div.box div.traffic table td {
1853 background: transparent;
1861 background: transparent;
1854 border: none;
1862 border: none;
1855 padding: 2px 3px 3px;
1863 padding: 2px 3px 3px;
1856 }
1864 }
1857
1865
1858 #content div.box div.traffic table td.legendLabel {
1866 #content div.box div.traffic table td.legendLabel {
1859 padding: 0 3px 2px;
1867 padding: 0 3px 2px;
1860 }
1868 }
1861
1869
1862 #summary {
1870 #summary {
1863
1871
1864 }
1872 }
1865
1873
1866 #summary .metatag {
1874 #summary .metatag {
1867 display: inline-block;
1875 display: inline-block;
1868 padding: 3px 5px;
1876 padding: 3px 5px;
1869 margin-bottom: 3px;
1877 margin-bottom: 3px;
1870 margin-right: 1px;
1878 margin-right: 1px;
1871 border-radius: 5px;
1879 border-radius: 5px;
1872 }
1880 }
1873
1881
1874 #content div.box #summary p {
1882 #content div.box #summary p {
1875 margin-bottom: -5px;
1883 margin-bottom: -5px;
1876 width: 600px;
1884 width: 600px;
1877 white-space: pre-wrap;
1885 white-space: pre-wrap;
1878 }
1886 }
1879
1887
1880 #content div.box #summary p:last-child {
1888 #content div.box #summary p:last-child {
1881 margin-bottom: 9px;
1889 margin-bottom: 9px;
1882 }
1890 }
1883
1891
1884 #content div.box #summary p:first-of-type {
1892 #content div.box #summary p:first-of-type {
1885 margin-top: 9px;
1893 margin-top: 9px;
1886 }
1894 }
1887
1895
1888 .metatag {
1896 .metatag {
1889 display: inline-block;
1897 display: inline-block;
1890 margin-right: 1px;
1898 margin-right: 1px;
1891 -webkit-border-radius: 4px 4px 4px 4px;
1899 -webkit-border-radius: 4px 4px 4px 4px;
1892 -khtml-border-radius: 4px 4px 4px 4px;
1900 -khtml-border-radius: 4px 4px 4px 4px;
1893 -moz-border-radius: 4px 4px 4px 4px;
1901 -moz-border-radius: 4px 4px 4px 4px;
1894 border-radius: 4px 4px 4px 4px;
1902 border-radius: 4px 4px 4px 4px;
1895
1903
1896 border: solid 1px #9CF;
1904 border: solid 1px #9CF;
1897 padding: 2px 3px 2px 3px !important;
1905 padding: 2px 3px 2px 3px !important;
1898 background-color: #DEF;
1906 background-color: #DEF;
1899 }
1907 }
1900
1908
1901 .metatag[tag="dead"] {
1909 .metatag[tag="dead"] {
1902 background-color: #E44;
1910 background-color: #E44;
1903 }
1911 }
1904
1912
1905 .metatag[tag="stale"] {
1913 .metatag[tag="stale"] {
1906 background-color: #EA4;
1914 background-color: #EA4;
1907 }
1915 }
1908
1916
1909 .metatag[tag="featured"] {
1917 .metatag[tag="featured"] {
1910 background-color: #AEA;
1918 background-color: #AEA;
1911 }
1919 }
1912
1920
1913 .metatag[tag="requires"] {
1921 .metatag[tag="requires"] {
1914 background-color: #9CF;
1922 background-color: #9CF;
1915 }
1923 }
1916
1924
1917 .metatag[tag="recommends"] {
1925 .metatag[tag="recommends"] {
1918 background-color: #BDF;
1926 background-color: #BDF;
1919 }
1927 }
1920
1928
1921 .metatag[tag="lang"] {
1929 .metatag[tag="lang"] {
1922 background-color: #FAF474;
1930 background-color: #FAF474;
1923 }
1931 }
1924
1932
1925 .metatag[tag="license"] {
1933 .metatag[tag="license"] {
1926 border: solid 1px #9CF;
1934 border: solid 1px #9CF;
1927 background-color: #DEF;
1935 background-color: #DEF;
1928 target-new: tab !important;
1936 target-new: tab !important;
1929 }
1937 }
1930 .metatag[tag="see"] {
1938 .metatag[tag="see"] {
1931 border: solid 1px #CBD;
1939 border: solid 1px #CBD;
1932 background-color: #EDF;
1940 background-color: #EDF;
1933 }
1941 }
1934
1942
1935 a.metatag[tag="license"]:hover {
1943 a.metatag[tag="license"]:hover {
1936 background-color: #003367;
1944 background-color: #003367;
1937 color: #FFF;
1945 color: #FFF;
1938 text-decoration: none;
1946 text-decoration: none;
1939 }
1947 }
1940
1948
1941 #summary .desc {
1949 #summary .desc {
1942 white-space: pre;
1950 white-space: pre;
1943 width: 100%;
1951 width: 100%;
1944 }
1952 }
1945
1953
1946 #summary .repo_name {
1954 #summary .repo_name {
1947 font-size: 1.6em;
1955 font-size: 1.6em;
1948 font-weight: bold;
1956 font-weight: bold;
1949 vertical-align: baseline;
1957 vertical-align: baseline;
1950 clear: right
1958 clear: right
1951 }
1959 }
1952
1960
1953 #footer {
1961 #footer {
1954 clear: both;
1962 clear: both;
1955 overflow: hidden;
1963 overflow: hidden;
1956 text-align: right;
1964 text-align: right;
1957 margin: 0;
1965 margin: 0;
1958 padding: 0 10px 4px;
1966 padding: 0 10px 4px;
1959 margin: -10px 0 0;
1967 margin: -10px 0 0;
1960 }
1968 }
1961
1969
1962 #footer div#footer-inner {
1970 #footer div#footer-inner {
1963 background-color: #003B76;
1971 background-color: #003B76;
1964 background-repeat : repeat-x;
1972 background-repeat : repeat-x;
1965 background-image : -khtml-gradient( linear, left top, left bottom, from(#003B76), to(#00376E));
1973 background-image : -khtml-gradient( linear, left top, left bottom, from(#003B76), to(#00376E));
1966 background-image : -moz-linear-gradient(top, #003b76, #00376e);
1974 background-image : -moz-linear-gradient(top, #003b76, #00376e);
1967 background-image : -ms-linear-gradient( top, #003b76, #00376e);
1975 background-image : -ms-linear-gradient( top, #003b76, #00376e);
1968 background-image : -webkit-gradient( linear, left top, left bottom, color-stop( 0%, #003b76), color-stop( 100%, #00376e));
1976 background-image : -webkit-gradient( linear, left top, left bottom, color-stop( 0%, #003b76), color-stop( 100%, #00376e));
1969 background-image : -webkit-linear-gradient( top, #003b76, #00376e));
1977 background-image : -webkit-linear-gradient( top, #003b76, #00376e));
1970 background-image : -o-linear-gradient( top, #003b76, #00376e));
1978 background-image : -o-linear-gradient( top, #003b76, #00376e));
1971 background-image : linear-gradient( top, #003b76, #00376e);
1979 background-image : linear-gradient( top, #003b76, #00376e);
1972 filter :progid : DXImageTransform.Microsoft.gradient ( startColorstr = '#003b76', endColorstr = '#00376e', GradientType = 0);
1980 filter :progid : DXImageTransform.Microsoft.gradient ( startColorstr = '#003b76', endColorstr = '#00376e', GradientType = 0);
1973 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
1981 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
1974 -webkit-border-radius: 4px 4px 4px 4px;
1982 -webkit-border-radius: 4px 4px 4px 4px;
1975 -khtml-border-radius: 4px 4px 4px 4px;
1983 -khtml-border-radius: 4px 4px 4px 4px;
1976 -moz-border-radius: 4px 4px 4px 4px;
1984 -moz-border-radius: 4px 4px 4px 4px;
1977 border-radius: 4px 4px 4px 4px;
1985 border-radius: 4px 4px 4px 4px;
1978 }
1986 }
1979
1987
1980 #footer div#footer-inner p {
1988 #footer div#footer-inner p {
1981 padding: 15px 25px 15px 0;
1989 padding: 15px 25px 15px 0;
1982 color: #FFF;
1990 color: #FFF;
1983 font-weight: 700;
1991 font-weight: 700;
1984 }
1992 }
1985
1993
1986 #footer div#footer-inner .footer-link {
1994 #footer div#footer-inner .footer-link {
1987 float: left;
1995 float: left;
1988 padding-left: 10px;
1996 padding-left: 10px;
1989 }
1997 }
1990
1998
1991 #footer div#footer-inner .footer-link a,#footer div#footer-inner .footer-link-right a
1999 #footer div#footer-inner .footer-link a,#footer div#footer-inner .footer-link-right a
1992 {
2000 {
1993 color: #FFF;
2001 color: #FFF;
1994 }
2002 }
1995
2003
1996 #login div.title {
2004 #login div.title {
1997 width: 420px;
2005 width: 420px;
1998 clear: both;
2006 clear: both;
1999 overflow: hidden;
2007 overflow: hidden;
2000 position: relative;
2008 position: relative;
2001 background-color: #003B76;
2009 background-color: #003B76;
2002 background-repeat : repeat-x;
2010 background-repeat : repeat-x;
2003 background-image : -khtml-gradient( linear, left top, left bottom, from(#003B76), to(#00376E));
2011 background-image : -khtml-gradient( linear, left top, left bottom, from(#003B76), to(#00376E));
2004 background-image : -moz-linear-gradient( top, #003b76, #00376e);
2012 background-image : -moz-linear-gradient( top, #003b76, #00376e);
2005 background-image : -ms-linear-gradient( top, #003b76, #00376e);
2013 background-image : -ms-linear-gradient( top, #003b76, #00376e);
2006 background-image : -webkit-gradient( linear, left top, left bottom, color-stop( 0%, #003b76), color-stop( 100%, #00376e));
2014 background-image : -webkit-gradient( linear, left top, left bottom, color-stop( 0%, #003b76), color-stop( 100%, #00376e));
2007 background-image : -webkit-linear-gradient( top, #003b76, #00376e));
2015 background-image : -webkit-linear-gradient( top, #003b76, #00376e));
2008 background-image : -o-linear-gradient( top, #003b76, #00376e));
2016 background-image : -o-linear-gradient( top, #003b76, #00376e));
2009 background-image : linear-gradient( top, #003b76, #00376e);
2017 background-image : linear-gradient( top, #003b76, #00376e);
2010 filter : progid : DXImageTransform.Microsoft.gradient ( startColorstr = '#003b76', endColorstr = '#00376e', GradientType = 0);
2018 filter : progid : DXImageTransform.Microsoft.gradient ( startColorstr = '#003b76', endColorstr = '#00376e', GradientType = 0);
2011 margin: 0 auto;
2019 margin: 0 auto;
2012 padding: 0;
2020 padding: 0;
2013 }
2021 }
2014
2022
2015 #login div.inner {
2023 #login div.inner {
2016 width: 380px;
2024 width: 380px;
2017 background: #FFF url("../images/login.png") no-repeat top left;
2025 background: #FFF url("../images/login.png") no-repeat top left;
2018 border-top: none;
2026 border-top: none;
2019 border-bottom: none;
2027 border-bottom: none;
2020 margin: 0 auto;
2028 margin: 0 auto;
2021 padding: 20px;
2029 padding: 20px;
2022 }
2030 }
2023
2031
2024 #login div.form div.fields div.field div.label {
2032 #login div.form div.fields div.field div.label {
2025 width: 173px;
2033 width: 173px;
2026 float: left;
2034 float: left;
2027 text-align: right;
2035 text-align: right;
2028 margin: 2px 10px 0 0;
2036 margin: 2px 10px 0 0;
2029 padding: 5px 0 0 5px;
2037 padding: 5px 0 0 5px;
2030 }
2038 }
2031
2039
2032 #login div.form div.fields div.field div.input input {
2040 #login div.form div.fields div.field div.input input {
2033 width: 176px;
2041 width: 176px;
2034 background: #FFF;
2042 background: #FFF;
2035 border-top: 1px solid #b3b3b3;
2043 border-top: 1px solid #b3b3b3;
2036 border-left: 1px solid #b3b3b3;
2044 border-left: 1px solid #b3b3b3;
2037 border-right: 1px solid #eaeaea;
2045 border-right: 1px solid #eaeaea;
2038 border-bottom: 1px solid #eaeaea;
2046 border-bottom: 1px solid #eaeaea;
2039 color: #000;
2047 color: #000;
2040 font-size: 11px;
2048 font-size: 11px;
2041 margin: 0;
2049 margin: 0;
2042 padding: 7px 7px 6px;
2050 padding: 7px 7px 6px;
2043 }
2051 }
2044
2052
2045 #login div.form div.fields div.buttons {
2053 #login div.form div.fields div.buttons {
2046 clear: both;
2054 clear: both;
2047 overflow: hidden;
2055 overflow: hidden;
2048 border-top: 1px solid #DDD;
2056 border-top: 1px solid #DDD;
2049 text-align: right;
2057 text-align: right;
2050 margin: 0;
2058 margin: 0;
2051 padding: 10px 0 0;
2059 padding: 10px 0 0;
2052 }
2060 }
2053
2061
2054 #login div.form div.links {
2062 #login div.form div.links {
2055 clear: both;
2063 clear: both;
2056 overflow: hidden;
2064 overflow: hidden;
2057 margin: 10px 0 0;
2065 margin: 10px 0 0;
2058 padding: 0 0 2px;
2066 padding: 0 0 2px;
2059 }
2067 }
2060
2068
2061 .user-menu{
2069 .user-menu{
2062 margin: 0px !important;
2070 margin: 0px !important;
2063 float: left;
2071 float: left;
2064 }
2072 }
2065
2073
2066 .user-menu .container{
2074 .user-menu .container{
2067 padding:0px 4px 0px 4px;
2075 padding:0px 4px 0px 4px;
2068 margin: 0px 0px 0px 0px;
2076 margin: 0px 0px 0px 0px;
2069 }
2077 }
2070
2078
2071 .user-menu .gravatar{
2079 .user-menu .gravatar{
2072 margin: 0px 0px 0px 0px;
2080 margin: 0px 0px 0px 0px;
2073 cursor: pointer;
2081 cursor: pointer;
2074 }
2082 }
2075 .user-menu .gravatar.enabled{
2083 .user-menu .gravatar.enabled{
2076 background-color: #FDF784 !important;
2084 background-color: #FDF784 !important;
2077 }
2085 }
2078 .user-menu .gravatar:hover{
2086 .user-menu .gravatar:hover{
2079 background-color: #FDF784 !important;
2087 background-color: #FDF784 !important;
2080 }
2088 }
2081 #quick_login{
2089 #quick_login{
2082 min-height: 80px;
2090 min-height: 80px;
2083 margin: 37px 0 0 -251px;
2091 margin: 37px 0 0 -251px;
2084 padding: 4px;
2092 padding: 4px;
2085 position: absolute;
2093 position: absolute;
2086 width: 278px;
2094 width: 278px;
2087 background-color: #003B76;
2095 background-color: #003B76;
2088 background-repeat: repeat-x;
2096 background-repeat: repeat-x;
2089 background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
2097 background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
2090 background-image: -moz-linear-gradient(top, #003b76, #00376e);
2098 background-image: -moz-linear-gradient(top, #003b76, #00376e);
2091 background-image: -ms-linear-gradient(top, #003b76, #00376e);
2099 background-image: -ms-linear-gradient(top, #003b76, #00376e);
2092 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76), color-stop(100%, #00376e) );
2100 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76), color-stop(100%, #00376e) );
2093 background-image: -webkit-linear-gradient(top, #003b76, #00376e);
2101 background-image: -webkit-linear-gradient(top, #003b76, #00376e);
2094 background-image: -o-linear-gradient(top, #003b76, #00376e);
2102 background-image: -o-linear-gradient(top, #003b76, #00376e);
2095 background-image: linear-gradient(top, #003b76, #00376e);
2103 background-image: linear-gradient(top, #003b76, #00376e);
2096 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76', endColorstr='#00376e', GradientType=0 );
2104 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76', endColorstr='#00376e', GradientType=0 );
2097
2105
2098 z-index: 999;
2106 z-index: 999;
2099 -webkit-border-radius: 0px 0px 4px 4px;
2107 -webkit-border-radius: 0px 0px 4px 4px;
2100 -khtml-border-radius: 0px 0px 4px 4px;
2108 -khtml-border-radius: 0px 0px 4px 4px;
2101 -moz-border-radius: 0px 0px 4px 4px;
2109 -moz-border-radius: 0px 0px 4px 4px;
2102 border-radius: 0px 0px 4px 4px;
2110 border-radius: 0px 0px 4px 4px;
2103 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
2111 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
2104 }
2112 }
2105 #quick_login h4{
2113 #quick_login h4{
2106 color: #fff;
2114 color: #fff;
2107 padding: 5px 0px 5px 14px;
2115 padding: 5px 0px 5px 14px;
2108 }
2116 }
2109
2117
2110 #quick_login .password_forgoten {
2118 #quick_login .password_forgoten {
2111 padding-right: 10px;
2119 padding-right: 10px;
2112 padding-top: 0px;
2120 padding-top: 0px;
2113 text-align: left;
2121 text-align: left;
2114 }
2122 }
2115
2123
2116 #quick_login .password_forgoten a {
2124 #quick_login .password_forgoten a {
2117 font-size: 10px;
2125 font-size: 10px;
2118 color: #fff;
2126 color: #fff;
2119 }
2127 }
2120
2128
2121 #quick_login .register {
2129 #quick_login .register {
2122 padding-right: 10px;
2130 padding-right: 10px;
2123 padding-top: 5px;
2131 padding-top: 5px;
2124 text-align: left;
2132 text-align: left;
2125 }
2133 }
2126
2134
2127 #quick_login .register a {
2135 #quick_login .register a {
2128 font-size: 10px;
2136 font-size: 10px;
2129 color: #fff;
2137 color: #fff;
2130 }
2138 }
2131
2139
2132 #quick_login .submit {
2140 #quick_login .submit {
2133 margin: -20px 0 0 0px;
2141 margin: -20px 0 0 0px;
2134 position: absolute;
2142 position: absolute;
2135 right: 15px;
2143 right: 15px;
2136 }
2144 }
2137
2145
2138 #quick_login .links_left{
2146 #quick_login .links_left{
2139 float: left;
2147 float: left;
2140 }
2148 }
2141 #quick_login .links_right{
2149 #quick_login .links_right{
2142 float: right;
2150 float: right;
2143 }
2151 }
2144 #quick_login .full_name{
2152 #quick_login .full_name{
2145 color: #FFFFFF;
2153 color: #FFFFFF;
2146 font-weight: bold;
2154 font-weight: bold;
2147 padding: 3px;
2155 padding: 3px;
2148 }
2156 }
2149 #quick_login .big_gravatar{
2157 #quick_login .big_gravatar{
2150 padding:4px 0px 0px 6px;
2158 padding:4px 0px 0px 6px;
2151 }
2159 }
2152 #quick_login .inbox{
2160 #quick_login .inbox{
2153 padding:4px 0px 0px 6px;
2161 padding:4px 0px 0px 6px;
2154 color: #FFFFFF;
2162 color: #FFFFFF;
2155 font-weight: bold;
2163 font-weight: bold;
2156 }
2164 }
2157 #quick_login .inbox a{
2165 #quick_login .inbox a{
2158 color: #FFFFFF;
2166 color: #FFFFFF;
2159 }
2167 }
2160 #quick_login .email,#quick_login .email a{
2168 #quick_login .email,#quick_login .email a{
2161 color: #FFFFFF;
2169 color: #FFFFFF;
2162 padding: 3px;
2170 padding: 3px;
2163
2171
2164 }
2172 }
2165 #quick_login .links .logout{
2173 #quick_login .links .logout{
2166
2174
2167 }
2175 }
2168
2176
2169 #quick_login div.form div.fields {
2177 #quick_login div.form div.fields {
2170 padding-top: 2px;
2178 padding-top: 2px;
2171 padding-left: 10px;
2179 padding-left: 10px;
2172 }
2180 }
2173
2181
2174 #quick_login div.form div.fields div.field {
2182 #quick_login div.form div.fields div.field {
2175 padding: 5px;
2183 padding: 5px;
2176 }
2184 }
2177
2185
2178 #quick_login div.form div.fields div.field div.label label {
2186 #quick_login div.form div.fields div.field div.label label {
2179 color: #fff;
2187 color: #fff;
2180 padding-bottom: 3px;
2188 padding-bottom: 3px;
2181 }
2189 }
2182
2190
2183 #quick_login div.form div.fields div.field div.input input {
2191 #quick_login div.form div.fields div.field div.input input {
2184 width: 236px;
2192 width: 236px;
2185 background: #FFF;
2193 background: #FFF;
2186 border-top: 1px solid #b3b3b3;
2194 border-top: 1px solid #b3b3b3;
2187 border-left: 1px solid #b3b3b3;
2195 border-left: 1px solid #b3b3b3;
2188 border-right: 1px solid #eaeaea;
2196 border-right: 1px solid #eaeaea;
2189 border-bottom: 1px solid #eaeaea;
2197 border-bottom: 1px solid #eaeaea;
2190 color: #000;
2198 color: #000;
2191 font-size: 11px;
2199 font-size: 11px;
2192 margin: 0;
2200 margin: 0;
2193 padding: 5px 7px 4px;
2201 padding: 5px 7px 4px;
2194 }
2202 }
2195
2203
2196 #quick_login div.form div.fields div.buttons {
2204 #quick_login div.form div.fields div.buttons {
2197 clear: both;
2205 clear: both;
2198 overflow: hidden;
2206 overflow: hidden;
2199 text-align: right;
2207 text-align: right;
2200 margin: 0;
2208 margin: 0;
2201 padding: 5px 14px 0px 5px;
2209 padding: 5px 14px 0px 5px;
2202 }
2210 }
2203
2211
2204 #quick_login div.form div.links {
2212 #quick_login div.form div.links {
2205 clear: both;
2213 clear: both;
2206 overflow: hidden;
2214 overflow: hidden;
2207 margin: 10px 0 0;
2215 margin: 10px 0 0;
2208 padding: 0 0 2px;
2216 padding: 0 0 2px;
2209 }
2217 }
2210
2218
2211 #quick_login ol.links{
2219 #quick_login ol.links{
2212 display: block;
2220 display: block;
2213 font-weight: bold;
2221 font-weight: bold;
2214 list-style: none outside none;
2222 list-style: none outside none;
2215 text-align: right;
2223 text-align: right;
2216 }
2224 }
2217 #quick_login ol.links li{
2225 #quick_login ol.links li{
2218 line-height: 27px;
2226 line-height: 27px;
2219 margin: 0;
2227 margin: 0;
2220 padding: 0;
2228 padding: 0;
2221 color: #fff;
2229 color: #fff;
2222 display: block;
2230 display: block;
2223 float:none !important;
2231 float:none !important;
2224 }
2232 }
2225
2233
2226 #quick_login ol.links li a{
2234 #quick_login ol.links li a{
2227 color: #fff;
2235 color: #fff;
2228 display: block;
2236 display: block;
2229 padding: 2px;
2237 padding: 2px;
2230 }
2238 }
2231 #quick_login ol.links li a:HOVER{
2239 #quick_login ol.links li a:HOVER{
2232 background-color: inherit !important;
2240 background-color: inherit !important;
2233 }
2241 }
2234
2242
2235 #register div.title {
2243 #register div.title {
2236 clear: both;
2244 clear: both;
2237 overflow: hidden;
2245 overflow: hidden;
2238 position: relative;
2246 position: relative;
2239 background-color: #003B76;
2247 background-color: #003B76;
2240 background-repeat: repeat-x;
2248 background-repeat: repeat-x;
2241 background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
2249 background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
2242 background-image: -moz-linear-gradient(top, #003b76, #00376e);
2250 background-image: -moz-linear-gradient(top, #003b76, #00376e);
2243 background-image: -ms-linear-gradient(top, #003b76, #00376e);
2251 background-image: -ms-linear-gradient(top, #003b76, #00376e);
2244 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76), color-stop(100%, #00376e) );
2252 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76), color-stop(100%, #00376e) );
2245 background-image: -webkit-linear-gradient(top, #003b76, #00376e);
2253 background-image: -webkit-linear-gradient(top, #003b76, #00376e);
2246 background-image: -o-linear-gradient(top, #003b76, #00376e);
2254 background-image: -o-linear-gradient(top, #003b76, #00376e);
2247 background-image: linear-gradient(top, #003b76, #00376e);
2255 background-image: linear-gradient(top, #003b76, #00376e);
2248 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',
2256 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',
2249 endColorstr='#00376e', GradientType=0 );
2257 endColorstr='#00376e', GradientType=0 );
2250 margin: 0 auto;
2258 margin: 0 auto;
2251 padding: 0;
2259 padding: 0;
2252 }
2260 }
2253
2261
2254 #register div.inner {
2262 #register div.inner {
2255 background: #FFF;
2263 background: #FFF;
2256 border-top: none;
2264 border-top: none;
2257 border-bottom: none;
2265 border-bottom: none;
2258 margin: 0 auto;
2266 margin: 0 auto;
2259 padding: 20px;
2267 padding: 20px;
2260 }
2268 }
2261
2269
2262 #register div.form div.fields div.field div.label {
2270 #register div.form div.fields div.field div.label {
2263 width: 135px;
2271 width: 135px;
2264 float: left;
2272 float: left;
2265 text-align: right;
2273 text-align: right;
2266 margin: 2px 10px 0 0;
2274 margin: 2px 10px 0 0;
2267 padding: 5px 0 0 5px;
2275 padding: 5px 0 0 5px;
2268 }
2276 }
2269
2277
2270 #register div.form div.fields div.field div.input input {
2278 #register div.form div.fields div.field div.input input {
2271 width: 300px;
2279 width: 300px;
2272 background: #FFF;
2280 background: #FFF;
2273 border-top: 1px solid #b3b3b3;
2281 border-top: 1px solid #b3b3b3;
2274 border-left: 1px solid #b3b3b3;
2282 border-left: 1px solid #b3b3b3;
2275 border-right: 1px solid #eaeaea;
2283 border-right: 1px solid #eaeaea;
2276 border-bottom: 1px solid #eaeaea;
2284 border-bottom: 1px solid #eaeaea;
2277 color: #000;
2285 color: #000;
2278 font-size: 11px;
2286 font-size: 11px;
2279 margin: 0;
2287 margin: 0;
2280 padding: 7px 7px 6px;
2288 padding: 7px 7px 6px;
2281 }
2289 }
2282
2290
2283 #register div.form div.fields div.buttons {
2291 #register div.form div.fields div.buttons {
2284 clear: both;
2292 clear: both;
2285 overflow: hidden;
2293 overflow: hidden;
2286 border-top: 1px solid #DDD;
2294 border-top: 1px solid #DDD;
2287 text-align: left;
2295 text-align: left;
2288 margin: 0;
2296 margin: 0;
2289 padding: 10px 0 0 150px;
2297 padding: 10px 0 0 150px;
2290 }
2298 }
2291
2299
2292 #register div.form div.activation_msg {
2300 #register div.form div.activation_msg {
2293 padding-top: 4px;
2301 padding-top: 4px;
2294 padding-bottom: 4px;
2302 padding-bottom: 4px;
2295 }
2303 }
2296
2304
2297 #journal .journal_day {
2305 #journal .journal_day {
2298 font-size: 20px;
2306 font-size: 20px;
2299 padding: 10px 0px;
2307 padding: 10px 0px;
2300 border-bottom: 2px solid #DDD;
2308 border-bottom: 2px solid #DDD;
2301 margin-left: 10px;
2309 margin-left: 10px;
2302 margin-right: 10px;
2310 margin-right: 10px;
2303 }
2311 }
2304
2312
2305 #journal .journal_container {
2313 #journal .journal_container {
2306 padding: 5px;
2314 padding: 5px;
2307 clear: both;
2315 clear: both;
2308 margin: 0px 5px 0px 10px;
2316 margin: 0px 5px 0px 10px;
2309 }
2317 }
2310
2318
2311 #journal .journal_action_container {
2319 #journal .journal_action_container {
2312 padding-left: 38px;
2320 padding-left: 38px;
2313 }
2321 }
2314
2322
2315 #journal .journal_user {
2323 #journal .journal_user {
2316 color: #747474;
2324 color: #747474;
2317 font-size: 14px;
2325 font-size: 14px;
2318 font-weight: bold;
2326 font-weight: bold;
2319 height: 30px;
2327 height: 30px;
2320 }
2328 }
2321
2329
2322 #journal .journal_icon {
2330 #journal .journal_icon {
2323 clear: both;
2331 clear: both;
2324 float: left;
2332 float: left;
2325 padding-right: 4px;
2333 padding-right: 4px;
2326 padding-top: 3px;
2334 padding-top: 3px;
2327 }
2335 }
2328
2336
2329 #journal .journal_action {
2337 #journal .journal_action {
2330 padding-top: 4px;
2338 padding-top: 4px;
2331 min-height: 2px;
2339 min-height: 2px;
2332 float: left
2340 float: left
2333 }
2341 }
2334
2342
2335 #journal .journal_action_params {
2343 #journal .journal_action_params {
2336 clear: left;
2344 clear: left;
2337 padding-left: 22px;
2345 padding-left: 22px;
2338 }
2346 }
2339
2347
2340 #journal .journal_repo {
2348 #journal .journal_repo {
2341 float: left;
2349 float: left;
2342 margin-left: 6px;
2350 margin-left: 6px;
2343 padding-top: 3px;
2351 padding-top: 3px;
2344 }
2352 }
2345
2353
2346 #journal .date {
2354 #journal .date {
2347 clear: both;
2355 clear: both;
2348 color: #777777;
2356 color: #777777;
2349 font-size: 11px;
2357 font-size: 11px;
2350 padding-left: 22px;
2358 padding-left: 22px;
2351 }
2359 }
2352
2360
2353 #journal .journal_repo .journal_repo_name {
2361 #journal .journal_repo .journal_repo_name {
2354 font-weight: bold;
2362 font-weight: bold;
2355 font-size: 1.1em;
2363 font-size: 1.1em;
2356 }
2364 }
2357
2365
2358 #journal .compare_view {
2366 #journal .compare_view {
2359 padding: 5px 0px 5px 0px;
2367 padding: 5px 0px 5px 0px;
2360 width: 95px;
2368 width: 95px;
2361 }
2369 }
2362
2370
2363 .journal_highlight {
2371 .journal_highlight {
2364 font-weight: bold;
2372 font-weight: bold;
2365 padding: 0 2px;
2373 padding: 0 2px;
2366 vertical-align: bottom;
2374 vertical-align: bottom;
2367 }
2375 }
2368
2376
2369 .trending_language_tbl,.trending_language_tbl td {
2377 .trending_language_tbl,.trending_language_tbl td {
2370 border: 0 !important;
2378 border: 0 !important;
2371 margin: 0 !important;
2379 margin: 0 !important;
2372 padding: 0 !important;
2380 padding: 0 !important;
2373 }
2381 }
2374
2382
2375 .trending_language_tbl,.trending_language_tbl tr {
2383 .trending_language_tbl,.trending_language_tbl tr {
2376 border-spacing: 1px;
2384 border-spacing: 1px;
2377 }
2385 }
2378
2386
2379 .trending_language {
2387 .trending_language {
2380 background-color: #003367;
2388 background-color: #003367;
2381 color: #FFF;
2389 color: #FFF;
2382 display: block;
2390 display: block;
2383 min-width: 20px;
2391 min-width: 20px;
2384 text-decoration: none;
2392 text-decoration: none;
2385 height: 12px;
2393 height: 12px;
2386 margin-bottom: 0px;
2394 margin-bottom: 0px;
2387 margin-left: 5px;
2395 margin-left: 5px;
2388 white-space: pre;
2396 white-space: pre;
2389 padding: 3px;
2397 padding: 3px;
2390 }
2398 }
2391
2399
2392 h3.files_location {
2400 h3.files_location {
2393 font-size: 1.8em;
2401 font-size: 1.8em;
2394 font-weight: 700;
2402 font-weight: 700;
2395 border-bottom: none !important;
2403 border-bottom: none !important;
2396 margin: 10px 0 !important;
2404 margin: 10px 0 !important;
2397 }
2405 }
2398
2406
2399 #files_data dl dt {
2407 #files_data dl dt {
2400 float: left;
2408 float: left;
2401 width: 60px;
2409 width: 60px;
2402 margin: 0 !important;
2410 margin: 0 !important;
2403 padding: 5px;
2411 padding: 5px;
2404 }
2412 }
2405
2413
2406 #files_data dl dd {
2414 #files_data dl dd {
2407 margin: 0 !important;
2415 margin: 0 !important;
2408 padding: 5px !important;
2416 padding: 5px !important;
2409 }
2417 }
2410
2418
2411 .file_history{
2419 .file_history{
2412 padding-top:10px;
2420 padding-top:10px;
2413 font-size:16px;
2421 font-size:16px;
2414 }
2422 }
2415 .file_author{
2423 .file_author{
2416 float: left;
2424 float: left;
2417 }
2425 }
2418
2426
2419 .file_author .item{
2427 .file_author .item{
2420 float:left;
2428 float:left;
2421 padding:5px;
2429 padding:5px;
2422 color: #888;
2430 color: #888;
2423 }
2431 }
2424
2432
2425 .tablerow0 {
2433 .tablerow0 {
2426 background-color: #F8F8F8;
2434 background-color: #F8F8F8;
2427 }
2435 }
2428
2436
2429 .tablerow1 {
2437 .tablerow1 {
2430 background-color: #FFFFFF;
2438 background-color: #FFFFFF;
2431 }
2439 }
2432
2440
2433 .changeset_id {
2441 .changeset_id {
2434 font-family: monospace;
2442 font-family: monospace;
2435 color: #666666;
2443 color: #666666;
2436 }
2444 }
2437
2445
2438 .changeset_hash {
2446 .changeset_hash {
2439 color: #000000;
2447 color: #000000;
2440 }
2448 }
2441
2449
2442 #changeset_content {
2450 #changeset_content {
2443 border-left: 1px solid #CCC;
2451 border-left: 1px solid #CCC;
2444 border-right: 1px solid #CCC;
2452 border-right: 1px solid #CCC;
2445 border-bottom: 1px solid #CCC;
2453 border-bottom: 1px solid #CCC;
2446 padding: 5px;
2454 padding: 5px;
2447 }
2455 }
2448
2456
2449 #changeset_compare_view_content {
2457 #changeset_compare_view_content {
2450 border: 1px solid #CCC;
2458 border: 1px solid #CCC;
2451 padding: 5px;
2459 padding: 5px;
2452 }
2460 }
2453
2461
2454 #changeset_content .container {
2462 #changeset_content .container {
2455 min-height: 100px;
2463 min-height: 100px;
2456 font-size: 1.2em;
2464 font-size: 1.2em;
2457 overflow: hidden;
2465 overflow: hidden;
2458 }
2466 }
2459
2467
2460 #changeset_compare_view_content .compare_view_commits {
2468 #changeset_compare_view_content .compare_view_commits {
2461 width: auto !important;
2469 width: auto !important;
2462 }
2470 }
2463
2471
2464 #changeset_compare_view_content .compare_view_commits td {
2472 #changeset_compare_view_content .compare_view_commits td {
2465 padding: 0px 0px 0px 12px !important;
2473 padding: 0px 0px 0px 12px !important;
2466 }
2474 }
2467
2475
2468 #changeset_content .container .right {
2476 #changeset_content .container .right {
2469 float: right;
2477 float: right;
2470 width: 20%;
2478 width: 20%;
2471 text-align: right;
2479 text-align: right;
2472 }
2480 }
2473
2481
2474 #changeset_content .container .left .message {
2482 #changeset_content .container .left .message {
2475 white-space: pre-wrap;
2483 white-space: pre-wrap;
2476 }
2484 }
2477 #changeset_content .container .left .message a:hover {
2485 #changeset_content .container .left .message a:hover {
2478 text-decoration: none;
2486 text-decoration: none;
2479 }
2487 }
2480 .cs_files .cur_cs {
2488 .cs_files .cur_cs {
2481 margin: 10px 2px;
2489 margin: 10px 2px;
2482 font-weight: bold;
2490 font-weight: bold;
2483 }
2491 }
2484
2492
2485 .cs_files .node {
2493 .cs_files .node {
2486 float: left;
2494 float: left;
2487 }
2495 }
2488
2496
2489 .cs_files .changes {
2497 .cs_files .changes {
2490 float: right;
2498 float: right;
2491 color:#003367;
2499 color:#003367;
2492
2500
2493 }
2501 }
2494
2502
2495 .cs_files .changes .added {
2503 .cs_files .changes .added {
2496 background-color: #BBFFBB;
2504 background-color: #BBFFBB;
2497 float: left;
2505 float: left;
2498 text-align: center;
2506 text-align: center;
2499 font-size: 9px;
2507 font-size: 9px;
2500 padding: 2px 0px 2px 0px;
2508 padding: 2px 0px 2px 0px;
2501 }
2509 }
2502
2510
2503 .cs_files .changes .deleted {
2511 .cs_files .changes .deleted {
2504 background-color: #FF8888;
2512 background-color: #FF8888;
2505 float: left;
2513 float: left;
2506 text-align: center;
2514 text-align: center;
2507 font-size: 9px;
2515 font-size: 9px;
2508 padding: 2px 0px 2px 0px;
2516 padding: 2px 0px 2px 0px;
2509 }
2517 }
2510 /*new binary*/
2518 /*new binary*/
2511 .cs_files .changes .bin1 {
2519 .cs_files .changes .bin1 {
2512 background-color: #BBFFBB;
2520 background-color: #BBFFBB;
2513 float: left;
2521 float: left;
2514 text-align: center;
2522 text-align: center;
2515 font-size: 9px;
2523 font-size: 9px;
2516 padding: 2px 0px 2px 0px;
2524 padding: 2px 0px 2px 0px;
2517 }
2525 }
2518
2526
2519 /*deleted binary*/
2527 /*deleted binary*/
2520 .cs_files .changes .bin2 {
2528 .cs_files .changes .bin2 {
2521 background-color: #FF8888;
2529 background-color: #FF8888;
2522 float: left;
2530 float: left;
2523 text-align: center;
2531 text-align: center;
2524 font-size: 9px;
2532 font-size: 9px;
2525 padding: 2px 0px 2px 0px;
2533 padding: 2px 0px 2px 0px;
2526 }
2534 }
2527
2535
2528 /*mod binary*/
2536 /*mod binary*/
2529 .cs_files .changes .bin3 {
2537 .cs_files .changes .bin3 {
2530 background-color: #DDDDDD;
2538 background-color: #DDDDDD;
2531 float: left;
2539 float: left;
2532 text-align: center;
2540 text-align: center;
2533 font-size: 9px;
2541 font-size: 9px;
2534 padding: 2px 0px 2px 0px;
2542 padding: 2px 0px 2px 0px;
2535 }
2543 }
2536
2544
2537 /*rename file*/
2545 /*rename file*/
2538 .cs_files .changes .bin4 {
2546 .cs_files .changes .bin4 {
2539 background-color: #6D99FF;
2547 background-color: #6D99FF;
2540 float: left;
2548 float: left;
2541 text-align: center;
2549 text-align: center;
2542 font-size: 9px;
2550 font-size: 9px;
2543 padding: 2px 0px 2px 0px;
2551 padding: 2px 0px 2px 0px;
2544 }
2552 }
2545
2553
2546
2554
2547 .cs_files .cs_added,.cs_files .cs_A {
2555 .cs_files .cs_added,.cs_files .cs_A {
2548 background: url("../images/icons/page_white_add.png") no-repeat scroll
2556 background: url("../images/icons/page_white_add.png") no-repeat scroll
2549 3px;
2557 3px;
2550 height: 16px;
2558 height: 16px;
2551 padding-left: 20px;
2559 padding-left: 20px;
2552 margin-top: 7px;
2560 margin-top: 7px;
2553 text-align: left;
2561 text-align: left;
2554 }
2562 }
2555
2563
2556 .cs_files .cs_changed,.cs_files .cs_M {
2564 .cs_files .cs_changed,.cs_files .cs_M {
2557 background: url("../images/icons/page_white_edit.png") no-repeat scroll
2565 background: url("../images/icons/page_white_edit.png") no-repeat scroll
2558 3px;
2566 3px;
2559 height: 16px;
2567 height: 16px;
2560 padding-left: 20px;
2568 padding-left: 20px;
2561 margin-top: 7px;
2569 margin-top: 7px;
2562 text-align: left;
2570 text-align: left;
2563 }
2571 }
2564
2572
2565 .cs_files .cs_removed,.cs_files .cs_D {
2573 .cs_files .cs_removed,.cs_files .cs_D {
2566 background: url("../images/icons/page_white_delete.png") no-repeat
2574 background: url("../images/icons/page_white_delete.png") no-repeat
2567 scroll 3px;
2575 scroll 3px;
2568 height: 16px;
2576 height: 16px;
2569 padding-left: 20px;
2577 padding-left: 20px;
2570 margin-top: 7px;
2578 margin-top: 7px;
2571 text-align: left;
2579 text-align: left;
2572 }
2580 }
2573
2581
2574 #graph {
2582 #graph {
2575 overflow: hidden;
2583 overflow: hidden;
2576 }
2584 }
2577
2585
2578 #graph_nodes {
2586 #graph_nodes {
2579 float: left;
2587 float: left;
2580 margin-right: 0px;
2588 margin-right: 0px;
2581 margin-top: 0px;
2589 margin-top: 0px;
2582 }
2590 }
2583
2591
2584 #graph_content {
2592 #graph_content {
2585 width: 80%;
2593 width: 80%;
2586 float: left;
2594 float: left;
2587 }
2595 }
2588
2596
2589 #graph_content .container_header {
2597 #graph_content .container_header {
2590 border-bottom: 1px solid #DDD;
2598 border-bottom: 1px solid #DDD;
2591 padding: 10px;
2599 padding: 10px;
2592 height: 25px;
2600 height: 25px;
2593 }
2601 }
2594
2602
2595 #graph_content #rev_range_container {
2603 #graph_content #rev_range_container {
2596 float: left;
2604 float: left;
2597 margin: 0px 0px 0px 3px;
2605 margin: 0px 0px 0px 3px;
2598 }
2606 }
2599
2607
2600 #graph_content #rev_range_clear {
2608 #graph_content #rev_range_clear {
2601 float: left;
2609 float: left;
2602 margin: 0px 0px 0px 3px;
2610 margin: 0px 0px 0px 3px;
2603 }
2611 }
2604
2612
2605 #graph_content .container {
2613 #graph_content .container {
2606 border-bottom: 1px solid #DDD;
2614 border-bottom: 1px solid #DDD;
2607 height: 56px;
2615 height: 56px;
2608 overflow: hidden;
2616 overflow: hidden;
2609 }
2617 }
2610
2618
2611 #graph_content .container .right {
2619 #graph_content .container .right {
2612 float: right;
2620 float: right;
2613 width: 23%;
2621 width: 23%;
2614 text-align: right;
2622 text-align: right;
2615 }
2623 }
2616
2624
2617 #graph_content .container .left {
2625 #graph_content .container .left {
2618 float: left;
2626 float: left;
2619 width: 25%;
2627 width: 25%;
2620 padding-left: 5px;
2628 padding-left: 5px;
2621 }
2629 }
2622
2630
2623 #graph_content .container .mid {
2631 #graph_content .container .mid {
2624 float: left;
2632 float: left;
2625 width: 49%;
2633 width: 49%;
2626 }
2634 }
2627
2635
2628
2636
2629 #graph_content .container .left .date {
2637 #graph_content .container .left .date {
2630 color: #666;
2638 color: #666;
2631 padding-left: 22px;
2639 padding-left: 22px;
2632 font-size: 10px;
2640 font-size: 10px;
2633 }
2641 }
2634
2642
2635 #graph_content .container .left .author {
2643 #graph_content .container .left .author {
2636 height: 22px;
2644 height: 22px;
2637 }
2645 }
2638
2646
2639 #graph_content .container .left .author .user {
2647 #graph_content .container .left .author .user {
2640 color: #444444;
2648 color: #444444;
2641 float: left;
2649 float: left;
2642 margin-left: -4px;
2650 margin-left: -4px;
2643 margin-top: 4px;
2651 margin-top: 4px;
2644 }
2652 }
2645
2653
2646 #graph_content .container .mid .message {
2654 #graph_content .container .mid .message {
2647 white-space: pre-wrap;
2655 white-space: pre-wrap;
2648 }
2656 }
2649
2657
2650 #graph_content .container .mid .message a:hover{
2658 #graph_content .container .mid .message a:hover{
2651 text-decoration: none;
2659 text-decoration: none;
2652 }
2660 }
2653
2661
2654 .revision-link
2662 .revision-link
2655 {
2663 {
2656 color:#3F6F9F;
2664 color:#3F6F9F;
2657 font-weight: bold !important;
2665 font-weight: bold !important;
2658 }
2666 }
2659
2667
2660 .issue-tracker-link{
2668 .issue-tracker-link{
2661 color:#3F6F9F;
2669 color:#3F6F9F;
2662 font-weight: bold !important;
2670 font-weight: bold !important;
2663 }
2671 }
2664
2672
2665 .changeset-status-container{
2673 .changeset-status-container{
2666 padding-right: 5px;
2674 padding-right: 5px;
2667 margin-top:1px;
2675 margin-top:1px;
2668 float:right;
2676 float:right;
2669 height:14px;
2677 height:14px;
2670 }
2678 }
2671 .code-header .changeset-status-container{
2679 .code-header .changeset-status-container{
2672 float:left;
2680 float:left;
2673 padding:2px 0px 0px 2px;
2681 padding:2px 0px 0px 2px;
2674 }
2682 }
2675 .changeset-status-container .changeset-status-lbl{
2683 .changeset-status-container .changeset-status-lbl{
2676 color: rgb(136, 136, 136);
2684 color: rgb(136, 136, 136);
2677 float: left;
2685 float: left;
2678 padding: 3px 4px 0px 0px
2686 padding: 3px 4px 0px 0px
2679 }
2687 }
2680 .code-header .changeset-status-container .changeset-status-lbl{
2688 .code-header .changeset-status-container .changeset-status-lbl{
2681 float: left;
2689 float: left;
2682 padding: 0px 4px 0px 0px;
2690 padding: 0px 4px 0px 0px;
2683 }
2691 }
2684 .changeset-status-container .changeset-status-ico{
2692 .changeset-status-container .changeset-status-ico{
2685 float: left;
2693 float: left;
2686 }
2694 }
2687 .code-header .changeset-status-container .changeset-status-ico, .container .changeset-status-ico{
2695 .code-header .changeset-status-container .changeset-status-ico, .container .changeset-status-ico{
2688 float: left;
2696 float: left;
2689 }
2697 }
2690 .right .comments-container{
2698 .right .comments-container{
2691 padding-right: 5px;
2699 padding-right: 5px;
2692 margin-top:1px;
2700 margin-top:1px;
2693 float:right;
2701 float:right;
2694 height:14px;
2702 height:14px;
2695 }
2703 }
2696
2704
2697 .right .comments-cnt{
2705 .right .comments-cnt{
2698 float: left;
2706 float: left;
2699 color: rgb(136, 136, 136);
2707 color: rgb(136, 136, 136);
2700 padding-right: 2px;
2708 padding-right: 2px;
2701 }
2709 }
2702
2710
2703 .right .changes{
2711 .right .changes{
2704 clear: both;
2712 clear: both;
2705 }
2713 }
2706
2714
2707 .right .changes .changed_total {
2715 .right .changes .changed_total {
2708 display: block;
2716 display: block;
2709 float: right;
2717 float: right;
2710 text-align: center;
2718 text-align: center;
2711 min-width: 45px;
2719 min-width: 45px;
2712 cursor: pointer;
2720 cursor: pointer;
2713 color: #444444;
2721 color: #444444;
2714 background: #FEA;
2722 background: #FEA;
2715 -webkit-border-radius: 0px 0px 0px 6px;
2723 -webkit-border-radius: 0px 0px 0px 6px;
2716 -moz-border-radius: 0px 0px 0px 6px;
2724 -moz-border-radius: 0px 0px 0px 6px;
2717 border-radius: 0px 0px 0px 6px;
2725 border-radius: 0px 0px 0px 6px;
2718 padding: 1px;
2726 padding: 1px;
2719 }
2727 }
2720
2728
2721 .right .changes .added,.changed,.removed {
2729 .right .changes .added,.changed,.removed {
2722 display: block;
2730 display: block;
2723 padding: 1px;
2731 padding: 1px;
2724 color: #444444;
2732 color: #444444;
2725 float: right;
2733 float: right;
2726 text-align: center;
2734 text-align: center;
2727 min-width: 15px;
2735 min-width: 15px;
2728 }
2736 }
2729
2737
2730 .right .changes .added {
2738 .right .changes .added {
2731 background: #CFC;
2739 background: #CFC;
2732 }
2740 }
2733
2741
2734 .right .changes .changed {
2742 .right .changes .changed {
2735 background: #FEA;
2743 background: #FEA;
2736 }
2744 }
2737
2745
2738 .right .changes .removed {
2746 .right .changes .removed {
2739 background: #FAA;
2747 background: #FAA;
2740 }
2748 }
2741
2749
2742 .right .merge {
2750 .right .merge {
2743 padding: 1px 3px 1px 3px;
2751 padding: 1px 3px 1px 3px;
2744 background-color: #fca062;
2752 background-color: #fca062;
2745 font-size: 10px;
2753 font-size: 10px;
2746 font-weight: bold;
2754 font-weight: bold;
2747 color: #ffffff;
2755 color: #ffffff;
2748 text-transform: uppercase;
2756 text-transform: uppercase;
2749 white-space: nowrap;
2757 white-space: nowrap;
2750 -webkit-border-radius: 3px;
2758 -webkit-border-radius: 3px;
2751 -moz-border-radius: 3px;
2759 -moz-border-radius: 3px;
2752 border-radius: 3px;
2760 border-radius: 3px;
2753 margin-right: 2px;
2761 margin-right: 2px;
2754 }
2762 }
2755
2763
2756 .right .parent {
2764 .right .parent {
2757 color: #666666;
2765 color: #666666;
2758 clear:both;
2766 clear:both;
2759 }
2767 }
2760 .right .logtags{
2768 .right .logtags{
2761 padding: 2px 2px 2px 2px;
2769 padding: 2px 2px 2px 2px;
2762 }
2770 }
2763 .right .logtags .branchtag,.right .logtags .tagtag,.right .logtags .booktag{
2771 .right .logtags .branchtag,.right .logtags .tagtag,.right .logtags .booktag{
2764 margin: 0px 2px;
2772 margin: 0px 2px;
2765 }
2773 }
2766
2774
2767 .right .logtags .branchtag,.logtags .branchtag {
2775 .right .logtags .branchtag,.logtags .branchtag {
2768 padding: 1px 3px 1px 3px;
2776 padding: 1px 3px 1px 3px;
2769 background-color: #bfbfbf;
2777 background-color: #bfbfbf;
2770 font-size: 10px;
2778 font-size: 10px;
2771 font-weight: bold;
2779 font-weight: bold;
2772 color: #ffffff;
2780 color: #ffffff;
2773 text-transform: uppercase;
2781 text-transform: uppercase;
2774 white-space: nowrap;
2782 white-space: nowrap;
2775 -webkit-border-radius: 3px;
2783 -webkit-border-radius: 3px;
2776 -moz-border-radius: 3px;
2784 -moz-border-radius: 3px;
2777 border-radius: 3px;
2785 border-radius: 3px;
2778 }
2786 }
2779 .right .logtags .branchtag a:hover,.logtags .branchtag a{
2787 .right .logtags .branchtag a:hover,.logtags .branchtag a{
2780 color: #ffffff;
2788 color: #ffffff;
2781 }
2789 }
2782 .right .logtags .branchtag a:hover,.logtags .branchtag a:hover{
2790 .right .logtags .branchtag a:hover,.logtags .branchtag a:hover{
2783 text-decoration: none;
2791 text-decoration: none;
2784 color: #ffffff;
2792 color: #ffffff;
2785 }
2793 }
2786 .right .logtags .tagtag,.logtags .tagtag {
2794 .right .logtags .tagtag,.logtags .tagtag {
2787 padding: 1px 3px 1px 3px;
2795 padding: 1px 3px 1px 3px;
2788 background-color: #62cffc;
2796 background-color: #62cffc;
2789 font-size: 10px;
2797 font-size: 10px;
2790 font-weight: bold;
2798 font-weight: bold;
2791 color: #ffffff;
2799 color: #ffffff;
2792 text-transform: uppercase;
2800 text-transform: uppercase;
2793 white-space: nowrap;
2801 white-space: nowrap;
2794 -webkit-border-radius: 3px;
2802 -webkit-border-radius: 3px;
2795 -moz-border-radius: 3px;
2803 -moz-border-radius: 3px;
2796 border-radius: 3px;
2804 border-radius: 3px;
2797 }
2805 }
2798 .right .logtags .tagtag a:hover,.logtags .tagtag a{
2806 .right .logtags .tagtag a:hover,.logtags .tagtag a{
2799 color: #ffffff;
2807 color: #ffffff;
2800 }
2808 }
2801 .right .logtags .tagtag a:hover,.logtags .tagtag a:hover{
2809 .right .logtags .tagtag a:hover,.logtags .tagtag a:hover{
2802 text-decoration: none;
2810 text-decoration: none;
2803 color: #ffffff;
2811 color: #ffffff;
2804 }
2812 }
2805 .right .logbooks .bookbook,.logbooks .bookbook,.right .logtags .bookbook,.logtags .bookbook {
2813 .right .logbooks .bookbook,.logbooks .bookbook,.right .logtags .bookbook,.logtags .bookbook {
2806 padding: 1px 3px 1px 3px;
2814 padding: 1px 3px 1px 3px;
2807 background-color: #46A546;
2815 background-color: #46A546;
2808 font-size: 10px;
2816 font-size: 10px;
2809 font-weight: bold;
2817 font-weight: bold;
2810 color: #ffffff;
2818 color: #ffffff;
2811 text-transform: uppercase;
2819 text-transform: uppercase;
2812 white-space: nowrap;
2820 white-space: nowrap;
2813 -webkit-border-radius: 3px;
2821 -webkit-border-radius: 3px;
2814 -moz-border-radius: 3px;
2822 -moz-border-radius: 3px;
2815 border-radius: 3px;
2823 border-radius: 3px;
2816 }
2824 }
2817 .right .logbooks .bookbook,.logbooks .bookbook a,.right .logtags .bookbook,.logtags .bookbook a{
2825 .right .logbooks .bookbook,.logbooks .bookbook a,.right .logtags .bookbook,.logtags .bookbook a{
2818 color: #ffffff;
2826 color: #ffffff;
2819 }
2827 }
2820 .right .logbooks .bookbook,.logbooks .bookbook a:hover,.right .logtags .bookbook,.logtags .bookbook a:hover{
2828 .right .logbooks .bookbook,.logbooks .bookbook a:hover,.right .logtags .bookbook,.logtags .bookbook a:hover{
2821 text-decoration: none;
2829 text-decoration: none;
2822 color: #ffffff;
2830 color: #ffffff;
2823 }
2831 }
2824 div.browserblock {
2832 div.browserblock {
2825 overflow: hidden;
2833 overflow: hidden;
2826 border: 1px solid #ccc;
2834 border: 1px solid #ccc;
2827 background: #f8f8f8;
2835 background: #f8f8f8;
2828 font-size: 100%;
2836 font-size: 100%;
2829 line-height: 125%;
2837 line-height: 125%;
2830 padding: 0;
2838 padding: 0;
2831 -webkit-border-radius: 6px 6px 0px 0px;
2839 -webkit-border-radius: 6px 6px 0px 0px;
2832 -moz-border-radius: 6px 6px 0px 0px;
2840 -moz-border-radius: 6px 6px 0px 0px;
2833 border-radius: 6px 6px 0px 0px;
2841 border-radius: 6px 6px 0px 0px;
2834 }
2842 }
2835
2843
2836 div.browserblock .browser-header {
2844 div.browserblock .browser-header {
2837 background: #FFF;
2845 background: #FFF;
2838 padding: 10px 0px 15px 0px;
2846 padding: 10px 0px 15px 0px;
2839 width: 100%;
2847 width: 100%;
2840 }
2848 }
2841
2849
2842 div.browserblock .browser-nav {
2850 div.browserblock .browser-nav {
2843 float: left
2851 float: left
2844 }
2852 }
2845
2853
2846 div.browserblock .browser-branch {
2854 div.browserblock .browser-branch {
2847 float: left;
2855 float: left;
2848 }
2856 }
2849
2857
2850 div.browserblock .browser-branch label {
2858 div.browserblock .browser-branch label {
2851 color: #4A4A4A;
2859 color: #4A4A4A;
2852 vertical-align: text-top;
2860 vertical-align: text-top;
2853 }
2861 }
2854
2862
2855 div.browserblock .browser-header span {
2863 div.browserblock .browser-header span {
2856 margin-left: 5px;
2864 margin-left: 5px;
2857 font-weight: 700;
2865 font-weight: 700;
2858 }
2866 }
2859
2867
2860 div.browserblock .browser-search {
2868 div.browserblock .browser-search {
2861 clear: both;
2869 clear: both;
2862 padding: 8px 8px 0px 5px;
2870 padding: 8px 8px 0px 5px;
2863 height: 20px;
2871 height: 20px;
2864 }
2872 }
2865
2873
2866 div.browserblock #node_filter_box {
2874 div.browserblock #node_filter_box {
2867
2875
2868 }
2876 }
2869
2877
2870 div.browserblock .search_activate {
2878 div.browserblock .search_activate {
2871 float: left
2879 float: left
2872 }
2880 }
2873
2881
2874 div.browserblock .add_node {
2882 div.browserblock .add_node {
2875 float: left;
2883 float: left;
2876 padding-left: 5px;
2884 padding-left: 5px;
2877 }
2885 }
2878
2886
2879 div.browserblock .search_activate a:hover,div.browserblock .add_node a:hover
2887 div.browserblock .search_activate a:hover,div.browserblock .add_node a:hover
2880 {
2888 {
2881 text-decoration: none !important;
2889 text-decoration: none !important;
2882 }
2890 }
2883
2891
2884 div.browserblock .browser-body {
2892 div.browserblock .browser-body {
2885 background: #EEE;
2893 background: #EEE;
2886 border-top: 1px solid #CCC;
2894 border-top: 1px solid #CCC;
2887 }
2895 }
2888
2896
2889 table.code-browser {
2897 table.code-browser {
2890 border-collapse: collapse;
2898 border-collapse: collapse;
2891 width: 100%;
2899 width: 100%;
2892 }
2900 }
2893
2901
2894 table.code-browser tr {
2902 table.code-browser tr {
2895 margin: 3px;
2903 margin: 3px;
2896 }
2904 }
2897
2905
2898 table.code-browser thead th {
2906 table.code-browser thead th {
2899 background-color: #EEE;
2907 background-color: #EEE;
2900 height: 20px;
2908 height: 20px;
2901 font-size: 1.1em;
2909 font-size: 1.1em;
2902 font-weight: 700;
2910 font-weight: 700;
2903 text-align: left;
2911 text-align: left;
2904 padding-left: 10px;
2912 padding-left: 10px;
2905 }
2913 }
2906
2914
2907 table.code-browser tbody td {
2915 table.code-browser tbody td {
2908 padding-left: 10px;
2916 padding-left: 10px;
2909 height: 20px;
2917 height: 20px;
2910 }
2918 }
2911
2919
2912 table.code-browser .browser-file {
2920 table.code-browser .browser-file {
2913 background: url("../images/icons/document_16.png") no-repeat scroll 3px;
2921 background: url("../images/icons/document_16.png") no-repeat scroll 3px;
2914 height: 16px;
2922 height: 16px;
2915 padding-left: 20px;
2923 padding-left: 20px;
2916 text-align: left;
2924 text-align: left;
2917 }
2925 }
2918 .diffblock .changeset_header {
2926 .diffblock .changeset_header {
2919 height: 16px;
2927 height: 16px;
2920 }
2928 }
2921 .diffblock .changeset_file {
2929 .diffblock .changeset_file {
2922 background: url("../images/icons/file.png") no-repeat scroll 3px;
2930 background: url("../images/icons/file.png") no-repeat scroll 3px;
2923 text-align: left;
2931 text-align: left;
2924 float: left;
2932 float: left;
2925 padding: 2px 0px 2px 22px;
2933 padding: 2px 0px 2px 22px;
2926 }
2934 }
2927 .diffblock .diff-menu-wrapper{
2935 .diffblock .diff-menu-wrapper{
2928 float: left;
2936 float: left;
2929 }
2937 }
2930
2938
2931 .diffblock .diff-menu{
2939 .diffblock .diff-menu{
2932 position: absolute;
2940 position: absolute;
2933 background: none repeat scroll 0 0 #FFFFFF;
2941 background: none repeat scroll 0 0 #FFFFFF;
2934 border-color: #003367 #666666 #666666;
2942 border-color: #003367 #666666 #666666;
2935 border-right: 1px solid #666666;
2943 border-right: 1px solid #666666;
2936 border-style: solid solid solid;
2944 border-style: solid solid solid;
2937 border-width: 1px;
2945 border-width: 1px;
2938 box-shadow: 2px 8px 4px rgba(0, 0, 0, 0.2);
2946 box-shadow: 2px 8px 4px rgba(0, 0, 0, 0.2);
2939 margin-top:5px;
2947 margin-top:5px;
2940 margin-left:1px;
2948 margin-left:1px;
2941
2949
2942 }
2950 }
2943 .diffblock .diff-actions {
2951 .diffblock .diff-actions {
2944 padding: 2px 0px 0px 2px;
2952 padding: 2px 0px 0px 2px;
2945 float: left;
2953 float: left;
2946 }
2954 }
2947 .diffblock .diff-menu ul li {
2955 .diffblock .diff-menu ul li {
2948 padding: 0px 0px 0px 0px !important;
2956 padding: 0px 0px 0px 0px !important;
2949 }
2957 }
2950 .diffblock .diff-menu ul li a{
2958 .diffblock .diff-menu ul li a{
2951 display: block;
2959 display: block;
2952 padding: 3px 8px 3px 8px !important;
2960 padding: 3px 8px 3px 8px !important;
2953 }
2961 }
2954 .diffblock .diff-menu ul li a:hover{
2962 .diffblock .diff-menu ul li a:hover{
2955 text-decoration: none;
2963 text-decoration: none;
2956 background-color: #EEEEEE;
2964 background-color: #EEEEEE;
2957 }
2965 }
2958 table.code-browser .browser-dir {
2966 table.code-browser .browser-dir {
2959 background: url("../images/icons/folder_16.png") no-repeat scroll 3px;
2967 background: url("../images/icons/folder_16.png") no-repeat scroll 3px;
2960 height: 16px;
2968 height: 16px;
2961 padding-left: 20px;
2969 padding-left: 20px;
2962 text-align: left;
2970 text-align: left;
2963 }
2971 }
2964
2972
2965 table.code-browser .submodule-dir {
2973 table.code-browser .submodule-dir {
2966 background: url("../images/icons/disconnect.png") no-repeat scroll 3px;
2974 background: url("../images/icons/disconnect.png") no-repeat scroll 3px;
2967 height: 16px;
2975 height: 16px;
2968 padding-left: 20px;
2976 padding-left: 20px;
2969 text-align: left;
2977 text-align: left;
2970 }
2978 }
2971
2979
2972
2980
2973 .box .search {
2981 .box .search {
2974 clear: both;
2982 clear: both;
2975 overflow: hidden;
2983 overflow: hidden;
2976 margin: 0;
2984 margin: 0;
2977 padding: 0 20px 10px;
2985 padding: 0 20px 10px;
2978 }
2986 }
2979
2987
2980 .box .search div.search_path {
2988 .box .search div.search_path {
2981 background: none repeat scroll 0 0 #EEE;
2989 background: none repeat scroll 0 0 #EEE;
2982 border: 1px solid #CCC;
2990 border: 1px solid #CCC;
2983 color: blue;
2991 color: blue;
2984 margin-bottom: 10px;
2992 margin-bottom: 10px;
2985 padding: 10px 0;
2993 padding: 10px 0;
2986 }
2994 }
2987
2995
2988 .box .search div.search_path div.link {
2996 .box .search div.search_path div.link {
2989 font-weight: 700;
2997 font-weight: 700;
2990 margin-left: 25px;
2998 margin-left: 25px;
2991 }
2999 }
2992
3000
2993 .box .search div.search_path div.link a {
3001 .box .search div.search_path div.link a {
2994 color: #003367;
3002 color: #003367;
2995 cursor: pointer;
3003 cursor: pointer;
2996 text-decoration: none;
3004 text-decoration: none;
2997 }
3005 }
2998
3006
2999 #path_unlock {
3007 #path_unlock {
3000 color: red;
3008 color: red;
3001 font-size: 1.2em;
3009 font-size: 1.2em;
3002 padding-left: 4px;
3010 padding-left: 4px;
3003 }
3011 }
3004
3012
3005 .info_box span {
3013 .info_box span {
3006 margin-left: 3px;
3014 margin-left: 3px;
3007 margin-right: 3px;
3015 margin-right: 3px;
3008 }
3016 }
3009
3017
3010 .info_box .rev {
3018 .info_box .rev {
3011 color: #003367;
3019 color: #003367;
3012 font-size: 1.6em;
3020 font-size: 1.6em;
3013 font-weight: bold;
3021 font-weight: bold;
3014 vertical-align: sub;
3022 vertical-align: sub;
3015 }
3023 }
3016
3024
3017 .info_box input#at_rev,.info_box input#size {
3025 .info_box input#at_rev,.info_box input#size {
3018 background: #FFF;
3026 background: #FFF;
3019 border-top: 1px solid #b3b3b3;
3027 border-top: 1px solid #b3b3b3;
3020 border-left: 1px solid #b3b3b3;
3028 border-left: 1px solid #b3b3b3;
3021 border-right: 1px solid #eaeaea;
3029 border-right: 1px solid #eaeaea;
3022 border-bottom: 1px solid #eaeaea;
3030 border-bottom: 1px solid #eaeaea;
3023 color: #000;
3031 color: #000;
3024 font-size: 12px;
3032 font-size: 12px;
3025 margin: 0;
3033 margin: 0;
3026 padding: 1px 5px 1px;
3034 padding: 1px 5px 1px;
3027 }
3035 }
3028
3036
3029 .info_box input#view {
3037 .info_box input#view {
3030 text-align: center;
3038 text-align: center;
3031 padding: 4px 3px 2px 2px;
3039 padding: 4px 3px 2px 2px;
3032 }
3040 }
3033
3041
3034 .yui-overlay,.yui-panel-container {
3042 .yui-overlay,.yui-panel-container {
3035 visibility: hidden;
3043 visibility: hidden;
3036 position: absolute;
3044 position: absolute;
3037 z-index: 2;
3045 z-index: 2;
3038 }
3046 }
3039
3047
3040 #tip-box {
3048 #tip-box {
3041 position: absolute;
3049 position: absolute;
3042
3050
3043 background-color: #FFF;
3051 background-color: #FFF;
3044 border: 2px solid #003367;
3052 border: 2px solid #003367;
3045 font: 100% sans-serif;
3053 font: 100% sans-serif;
3046 width: auto;
3054 width: auto;
3047 opacity: 1px;
3055 opacity: 1px;
3048 padding: 8px;
3056 padding: 8px;
3049
3057
3050 white-space: pre-wrap;
3058 white-space: pre-wrap;
3051 -webkit-border-radius: 8px 8px 8px 8px;
3059 -webkit-border-radius: 8px 8px 8px 8px;
3052 -khtml-border-radius: 8px 8px 8px 8px;
3060 -khtml-border-radius: 8px 8px 8px 8px;
3053 -moz-border-radius: 8px 8px 8px 8px;
3061 -moz-border-radius: 8px 8px 8px 8px;
3054 border-radius: 8px 8px 8px 8px;
3062 border-radius: 8px 8px 8px 8px;
3055 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
3063 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
3056 -moz-box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
3064 -moz-box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
3057 -webkit-box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
3065 -webkit-box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
3058 }
3066 }
3059
3067
3060 .mentions-container{
3068 .mentions-container{
3061 width: 90% !important;
3069 width: 90% !important;
3062 }
3070 }
3063 .mentions-container .yui-ac-content{
3071 .mentions-container .yui-ac-content{
3064 width: 100% !important;
3072 width: 100% !important;
3065 }
3073 }
3066
3074
3067 .ac {
3075 .ac {
3068 vertical-align: top;
3076 vertical-align: top;
3069 }
3077 }
3070
3078
3071 .ac .yui-ac {
3079 .ac .yui-ac {
3072 position: inherit;
3080 position: inherit;
3073 font-size: 100%;
3081 font-size: 100%;
3074 }
3082 }
3075
3083
3076 .ac .perm_ac {
3084 .ac .perm_ac {
3077 width: 20em;
3085 width: 20em;
3078 }
3086 }
3079
3087
3080 .ac .yui-ac-input {
3088 .ac .yui-ac-input {
3081 width: 100%;
3089 width: 100%;
3082 }
3090 }
3083
3091
3084 .ac .yui-ac-container {
3092 .ac .yui-ac-container {
3085 position: absolute;
3093 position: absolute;
3086 top: 1.6em;
3094 top: 1.6em;
3087 width: auto;
3095 width: auto;
3088 }
3096 }
3089
3097
3090 .ac .yui-ac-content {
3098 .ac .yui-ac-content {
3091 position: absolute;
3099 position: absolute;
3092 border: 1px solid gray;
3100 border: 1px solid gray;
3093 background: #fff;
3101 background: #fff;
3094 z-index: 9050;
3102 z-index: 9050;
3095
3103
3096 }
3104 }
3097
3105
3098 .ac .yui-ac-shadow {
3106 .ac .yui-ac-shadow {
3099 position: absolute;
3107 position: absolute;
3100 width: 100%;
3108 width: 100%;
3101 background: #000;
3109 background: #000;
3102 -moz-opacity: 0.1px;
3110 -moz-opacity: 0.1px;
3103 opacity: .10;
3111 opacity: .10;
3104 filter: alpha(opacity = 10);
3112 filter: alpha(opacity = 10);
3105 z-index: 9049;
3113 z-index: 9049;
3106 margin: .3em;
3114 margin: .3em;
3107 }
3115 }
3108
3116
3109 .ac .yui-ac-content ul {
3117 .ac .yui-ac-content ul {
3110 width: 100%;
3118 width: 100%;
3111 margin: 0;
3119 margin: 0;
3112 padding: 0;
3120 padding: 0;
3113 z-index: 9050;
3121 z-index: 9050;
3114 }
3122 }
3115
3123
3116 .ac .yui-ac-content li {
3124 .ac .yui-ac-content li {
3117 cursor: default;
3125 cursor: default;
3118 white-space: nowrap;
3126 white-space: nowrap;
3119 margin: 0;
3127 margin: 0;
3120 padding: 2px 5px;
3128 padding: 2px 5px;
3121 height: 18px;
3129 height: 18px;
3122 z-index: 9050;
3130 z-index: 9050;
3123 display: block;
3131 display: block;
3124 width: auto !important;
3132 width: auto !important;
3125 }
3133 }
3126
3134
3127 .ac .yui-ac-content li .ac-container-wrap{
3135 .ac .yui-ac-content li .ac-container-wrap{
3128 width: auto;
3136 width: auto;
3129 }
3137 }
3130
3138
3131 .ac .yui-ac-content li.yui-ac-prehighlight {
3139 .ac .yui-ac-content li.yui-ac-prehighlight {
3132 background: #B3D4FF;
3140 background: #B3D4FF;
3133 z-index: 9050;
3141 z-index: 9050;
3134 }
3142 }
3135
3143
3136 .ac .yui-ac-content li.yui-ac-highlight {
3144 .ac .yui-ac-content li.yui-ac-highlight {
3137 background: #556CB5;
3145 background: #556CB5;
3138 color: #FFF;
3146 color: #FFF;
3139 z-index: 9050;
3147 z-index: 9050;
3140 }
3148 }
3141 .ac .yui-ac-bd{
3149 .ac .yui-ac-bd{
3142 z-index: 9050;
3150 z-index: 9050;
3143 }
3151 }
3144
3152
3145 .follow {
3153 .follow {
3146 background: url("../images/icons/heart_add.png") no-repeat scroll 3px;
3154 background: url("../images/icons/heart_add.png") no-repeat scroll 3px;
3147 height: 16px;
3155 height: 16px;
3148 width: 20px;
3156 width: 20px;
3149 cursor: pointer;
3157 cursor: pointer;
3150 display: block;
3158 display: block;
3151 float: right;
3159 float: right;
3152 margin-top: 2px;
3160 margin-top: 2px;
3153 }
3161 }
3154
3162
3155 .following {
3163 .following {
3156 background: url("../images/icons/heart_delete.png") no-repeat scroll 3px;
3164 background: url("../images/icons/heart_delete.png") no-repeat scroll 3px;
3157 height: 16px;
3165 height: 16px;
3158 width: 20px;
3166 width: 20px;
3159 cursor: pointer;
3167 cursor: pointer;
3160 display: block;
3168 display: block;
3161 float: right;
3169 float: right;
3162 margin-top: 2px;
3170 margin-top: 2px;
3163 }
3171 }
3164
3172
3165 .locking_locked{
3173 .locking_locked{
3166 background: #FFF url("../images/icons/block_16.png") no-repeat scroll 3px;
3174 background: #FFF url("../images/icons/block_16.png") no-repeat scroll 3px;
3167 height: 16px;
3175 height: 16px;
3168 width: 20px;
3176 width: 20px;
3169 cursor: pointer;
3177 cursor: pointer;
3170 display: block;
3178 display: block;
3171 float: right;
3179 float: right;
3172 margin-top: 2px;
3180 margin-top: 2px;
3173 }
3181 }
3174
3182
3175 .locking_unlocked{
3183 .locking_unlocked{
3176 background: #FFF url("../images/icons/accept.png") no-repeat scroll 3px;
3184 background: #FFF url("../images/icons/accept.png") no-repeat scroll 3px;
3177 height: 16px;
3185 height: 16px;
3178 width: 20px;
3186 width: 20px;
3179 cursor: pointer;
3187 cursor: pointer;
3180 display: block;
3188 display: block;
3181 float: right;
3189 float: right;
3182 margin-top: 2px;
3190 margin-top: 2px;
3183 }
3191 }
3184
3192
3185 .currently_following {
3193 .currently_following {
3186 padding-left: 10px;
3194 padding-left: 10px;
3187 padding-bottom: 5px;
3195 padding-bottom: 5px;
3188 }
3196 }
3189
3197
3190 .add_icon {
3198 .add_icon {
3191 background: url("../images/icons/add.png") no-repeat scroll 3px;
3199 background: url("../images/icons/add.png") no-repeat scroll 3px;
3192 padding-left: 20px;
3200 padding-left: 20px;
3193 padding-top: 0px;
3201 padding-top: 0px;
3194 text-align: left;
3202 text-align: left;
3195 }
3203 }
3196
3204
3197 .accept_icon {
3205 .accept_icon {
3198 background: url("../images/icons/accept.png") no-repeat scroll 3px;
3206 background: url("../images/icons/accept.png") no-repeat scroll 3px;
3199 padding-left: 20px;
3207 padding-left: 20px;
3200 padding-top: 0px;
3208 padding-top: 0px;
3201 text-align: left;
3209 text-align: left;
3202 }
3210 }
3203
3211
3204 .edit_icon {
3212 .edit_icon {
3205 background: url("../images/icons/folder_edit.png") no-repeat scroll 3px;
3213 background: url("../images/icons/folder_edit.png") no-repeat scroll 3px;
3206 padding-left: 20px;
3214 padding-left: 20px;
3207 padding-top: 0px;
3215 padding-top: 0px;
3208 text-align: left;
3216 text-align: left;
3209 }
3217 }
3210
3218
3211 .delete_icon {
3219 .delete_icon {
3212 background: url("../images/icons/delete.png") no-repeat scroll 3px;
3220 background: url("../images/icons/delete.png") no-repeat scroll 3px;
3213 padding-left: 20px;
3221 padding-left: 20px;
3214 padding-top: 0px;
3222 padding-top: 0px;
3215 text-align: left;
3223 text-align: left;
3216 }
3224 }
3217
3225
3218 .refresh_icon {
3226 .refresh_icon {
3219 background: url("../images/icons/arrow_refresh.png") no-repeat scroll
3227 background: url("../images/icons/arrow_refresh.png") no-repeat scroll
3220 3px;
3228 3px;
3221 padding-left: 20px;
3229 padding-left: 20px;
3222 padding-top: 0px;
3230 padding-top: 0px;
3223 text-align: left;
3231 text-align: left;
3224 }
3232 }
3225
3233
3226 .pull_icon {
3234 .pull_icon {
3227 background: url("../images/icons/connect.png") no-repeat scroll 3px;
3235 background: url("../images/icons/connect.png") no-repeat scroll 3px;
3228 padding-left: 20px;
3236 padding-left: 20px;
3229 padding-top: 0px;
3237 padding-top: 0px;
3230 text-align: left;
3238 text-align: left;
3231 }
3239 }
3232
3240
3233 .rss_icon {
3241 .rss_icon {
3234 background: url("../images/icons/rss_16.png") no-repeat scroll 3px;
3242 background: url("../images/icons/rss_16.png") no-repeat scroll 3px;
3235 padding-left: 20px;
3243 padding-left: 20px;
3236 padding-top: 4px;
3244 padding-top: 4px;
3237 text-align: left;
3245 text-align: left;
3238 font-size: 8px
3246 font-size: 8px
3239 }
3247 }
3240
3248
3241 .atom_icon {
3249 .atom_icon {
3242 background: url("../images/icons/atom.png") no-repeat scroll 3px;
3250 background: url("../images/icons/atom.png") no-repeat scroll 3px;
3243 padding-left: 20px;
3251 padding-left: 20px;
3244 padding-top: 4px;
3252 padding-top: 4px;
3245 text-align: left;
3253 text-align: left;
3246 font-size: 8px
3254 font-size: 8px
3247 }
3255 }
3248
3256
3249 .archive_icon {
3257 .archive_icon {
3250 background: url("../images/icons/compress.png") no-repeat scroll 3px;
3258 background: url("../images/icons/compress.png") no-repeat scroll 3px;
3251 padding-left: 20px;
3259 padding-left: 20px;
3252 text-align: left;
3260 text-align: left;
3253 padding-top: 1px;
3261 padding-top: 1px;
3254 }
3262 }
3255
3263
3256 .start_following_icon {
3264 .start_following_icon {
3257 background: url("../images/icons/heart_add.png") no-repeat scroll 3px;
3265 background: url("../images/icons/heart_add.png") no-repeat scroll 3px;
3258 padding-left: 20px;
3266 padding-left: 20px;
3259 text-align: left;
3267 text-align: left;
3260 padding-top: 0px;
3268 padding-top: 0px;
3261 }
3269 }
3262
3270
3263 .stop_following_icon {
3271 .stop_following_icon {
3264 background: url("../images/icons/heart_delete.png") no-repeat scroll 3px;
3272 background: url("../images/icons/heart_delete.png") no-repeat scroll 3px;
3265 padding-left: 20px;
3273 padding-left: 20px;
3266 text-align: left;
3274 text-align: left;
3267 padding-top: 0px;
3275 padding-top: 0px;
3268 }
3276 }
3269
3277
3270 .action_button {
3278 .action_button {
3271 border: 0;
3279 border: 0;
3272 display: inline;
3280 display: inline;
3273 }
3281 }
3274
3282
3275 .action_button:hover {
3283 .action_button:hover {
3276 border: 0;
3284 border: 0;
3277 text-decoration: underline;
3285 text-decoration: underline;
3278 cursor: pointer;
3286 cursor: pointer;
3279 }
3287 }
3280
3288
3281 #switch_repos {
3289 #switch_repos {
3282 position: absolute;
3290 position: absolute;
3283 height: 25px;
3291 height: 25px;
3284 z-index: 1;
3292 z-index: 1;
3285 }
3293 }
3286
3294
3287 #switch_repos select {
3295 #switch_repos select {
3288 min-width: 150px;
3296 min-width: 150px;
3289 max-height: 250px;
3297 max-height: 250px;
3290 z-index: 1;
3298 z-index: 1;
3291 }
3299 }
3292
3300
3293 .breadcrumbs {
3301 .breadcrumbs {
3294 border: medium none;
3302 border: medium none;
3295 color: #FFF;
3303 color: #FFF;
3296 float: left;
3304 float: left;
3297 text-transform: uppercase;
3305 text-transform: uppercase;
3298 font-weight: 700;
3306 font-weight: 700;
3299 font-size: 14px;
3307 font-size: 14px;
3300 margin: 0;
3308 margin: 0;
3301 padding: 11px 0 11px 10px;
3309 padding: 11px 0 11px 10px;
3302 }
3310 }
3303
3311
3304 .breadcrumbs .hash {
3312 .breadcrumbs .hash {
3305 text-transform: none;
3313 text-transform: none;
3306 color: #fff;
3314 color: #fff;
3307 }
3315 }
3308
3316
3309 .breadcrumbs a {
3317 .breadcrumbs a {
3310 color: #FFF;
3318 color: #FFF;
3311 }
3319 }
3312
3320
3313 .flash_msg {
3321 .flash_msg {
3314
3322
3315 }
3323 }
3316
3324
3317 .flash_msg ul {
3325 .flash_msg ul {
3318
3326
3319 }
3327 }
3320
3328
3321 .error_red {
3329 .error_red {
3322 color:red;
3330 color:red;
3323 }
3331 }
3324
3332
3325 .error_msg {
3333 .error_msg {
3326 background-color: #c43c35;
3334 background-color: #c43c35;
3327 background-repeat: repeat-x;
3335 background-repeat: repeat-x;
3328 background-image: -khtml-gradient(linear, left top, left bottom, from(#ee5f5b), to(#c43c35) );
3336 background-image: -khtml-gradient(linear, left top, left bottom, from(#ee5f5b), to(#c43c35) );
3329 background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35);
3337 background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35);
3330 background-image: -ms-linear-gradient(top, #ee5f5b, #c43c35);
3338 background-image: -ms-linear-gradient(top, #ee5f5b, #c43c35);
3331 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ee5f5b), color-stop(100%, #c43c35) );
3339 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ee5f5b), color-stop(100%, #c43c35) );
3332 background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35);
3340 background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35);
3333 background-image: -o-linear-gradient(top, #ee5f5b, #c43c35);
3341 background-image: -o-linear-gradient(top, #ee5f5b, #c43c35);
3334 background-image: linear-gradient(top, #ee5f5b, #c43c35);
3342 background-image: linear-gradient(top, #ee5f5b, #c43c35);
3335 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b',endColorstr='#c43c35', GradientType=0 );
3343 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b',endColorstr='#c43c35', GradientType=0 );
3336 border-color: #c43c35 #c43c35 #882a25;
3344 border-color: #c43c35 #c43c35 #882a25;
3337 }
3345 }
3338
3346
3339 .warning_msg {
3347 .warning_msg {
3340 color: #404040 !important;
3348 color: #404040 !important;
3341 background-color: #eedc94;
3349 background-color: #eedc94;
3342 background-repeat: repeat-x;
3350 background-repeat: repeat-x;
3343 background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1), to(#eedc94) );
3351 background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1), to(#eedc94) );
3344 background-image: -moz-linear-gradient(top, #fceec1, #eedc94);
3352 background-image: -moz-linear-gradient(top, #fceec1, #eedc94);
3345 background-image: -ms-linear-gradient(top, #fceec1, #eedc94);
3353 background-image: -ms-linear-gradient(top, #fceec1, #eedc94);
3346 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fceec1), color-stop(100%, #eedc94) );
3354 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fceec1), color-stop(100%, #eedc94) );
3347 background-image: -webkit-linear-gradient(top, #fceec1, #eedc94);
3355 background-image: -webkit-linear-gradient(top, #fceec1, #eedc94);
3348 background-image: -o-linear-gradient(top, #fceec1, #eedc94);
3356 background-image: -o-linear-gradient(top, #fceec1, #eedc94);
3349 background-image: linear-gradient(top, #fceec1, #eedc94);
3357 background-image: linear-gradient(top, #fceec1, #eedc94);
3350 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fceec1', endColorstr='#eedc94', GradientType=0 );
3358 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fceec1', endColorstr='#eedc94', GradientType=0 );
3351 border-color: #eedc94 #eedc94 #e4c652;
3359 border-color: #eedc94 #eedc94 #e4c652;
3352 }
3360 }
3353
3361
3354 .success_msg {
3362 .success_msg {
3355 background-color: #57a957;
3363 background-color: #57a957;
3356 background-repeat: repeat-x !important;
3364 background-repeat: repeat-x !important;
3357 background-image: -khtml-gradient(linear, left top, left bottom, from(#62c462), to(#57a957) );
3365 background-image: -khtml-gradient(linear, left top, left bottom, from(#62c462), to(#57a957) );
3358 background-image: -moz-linear-gradient(top, #62c462, #57a957);
3366 background-image: -moz-linear-gradient(top, #62c462, #57a957);
3359 background-image: -ms-linear-gradient(top, #62c462, #57a957);
3367 background-image: -ms-linear-gradient(top, #62c462, #57a957);
3360 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #62c462), color-stop(100%, #57a957) );
3368 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #62c462), color-stop(100%, #57a957) );
3361 background-image: -webkit-linear-gradient(top, #62c462, #57a957);
3369 background-image: -webkit-linear-gradient(top, #62c462, #57a957);
3362 background-image: -o-linear-gradient(top, #62c462, #57a957);
3370 background-image: -o-linear-gradient(top, #62c462, #57a957);
3363 background-image: linear-gradient(top, #62c462, #57a957);
3371 background-image: linear-gradient(top, #62c462, #57a957);
3364 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0 );
3372 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0 );
3365 border-color: #57a957 #57a957 #3d773d;
3373 border-color: #57a957 #57a957 #3d773d;
3366 }
3374 }
3367
3375
3368 .notice_msg {
3376 .notice_msg {
3369 background-color: #339bb9;
3377 background-color: #339bb9;
3370 background-repeat: repeat-x;
3378 background-repeat: repeat-x;
3371 background-image: -khtml-gradient(linear, left top, left bottom, from(#5bc0de), to(#339bb9) );
3379 background-image: -khtml-gradient(linear, left top, left bottom, from(#5bc0de), to(#339bb9) );
3372 background-image: -moz-linear-gradient(top, #5bc0de, #339bb9);
3380 background-image: -moz-linear-gradient(top, #5bc0de, #339bb9);
3373 background-image: -ms-linear-gradient(top, #5bc0de, #339bb9);
3381 background-image: -ms-linear-gradient(top, #5bc0de, #339bb9);
3374 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #5bc0de), color-stop(100%, #339bb9) );
3382 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #5bc0de), color-stop(100%, #339bb9) );
3375 background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9);
3383 background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9);
3376 background-image: -o-linear-gradient(top, #5bc0de, #339bb9);
3384 background-image: -o-linear-gradient(top, #5bc0de, #339bb9);
3377 background-image: linear-gradient(top, #5bc0de, #339bb9);
3385 background-image: linear-gradient(top, #5bc0de, #339bb9);
3378 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0 );
3386 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0 );
3379 border-color: #339bb9 #339bb9 #22697d;
3387 border-color: #339bb9 #339bb9 #22697d;
3380 }
3388 }
3381
3389
3382 .success_msg,.error_msg,.notice_msg,.warning_msg {
3390 .success_msg,.error_msg,.notice_msg,.warning_msg {
3383 font-size: 12px;
3391 font-size: 12px;
3384 font-weight: 700;
3392 font-weight: 700;
3385 min-height: 14px;
3393 min-height: 14px;
3386 line-height: 14px;
3394 line-height: 14px;
3387 margin-bottom: 10px;
3395 margin-bottom: 10px;
3388 margin-top: 0;
3396 margin-top: 0;
3389 display: block;
3397 display: block;
3390 overflow: auto;
3398 overflow: auto;
3391 padding: 6px 10px 6px 10px;
3399 padding: 6px 10px 6px 10px;
3392 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
3400 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
3393 position: relative;
3401 position: relative;
3394 color: #FFF;
3402 color: #FFF;
3395 border-width: 1px;
3403 border-width: 1px;
3396 border-style: solid;
3404 border-style: solid;
3397 -webkit-border-radius: 4px;
3405 -webkit-border-radius: 4px;
3398 -moz-border-radius: 4px;
3406 -moz-border-radius: 4px;
3399 border-radius: 4px;
3407 border-radius: 4px;
3400 -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
3408 -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
3401 -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
3409 -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
3402 box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
3410 box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
3403 }
3411 }
3404
3412
3405 #msg_close {
3413 #msg_close {
3406 background: transparent url("../icons/cross_grey_small.png") no-repeat scroll 0 0;
3414 background: transparent url("../icons/cross_grey_small.png") no-repeat scroll 0 0;
3407 cursor: pointer;
3415 cursor: pointer;
3408 height: 16px;
3416 height: 16px;
3409 position: absolute;
3417 position: absolute;
3410 right: 5px;
3418 right: 5px;
3411 top: 5px;
3419 top: 5px;
3412 width: 16px;
3420 width: 16px;
3413 }
3421 }
3414 div#legend_data{
3422 div#legend_data{
3415 padding-left:10px;
3423 padding-left:10px;
3416 }
3424 }
3417 div#legend_container table{
3425 div#legend_container table{
3418 border: none !important;
3426 border: none !important;
3419 }
3427 }
3420 div#legend_container table,div#legend_choices table {
3428 div#legend_container table,div#legend_choices table {
3421 width: auto !important;
3429 width: auto !important;
3422 }
3430 }
3423
3431
3424 table#permissions_manage {
3432 table#permissions_manage {
3425 width: 0 !important;
3433 width: 0 !important;
3426 }
3434 }
3427
3435
3428 table#permissions_manage span.private_repo_msg {
3436 table#permissions_manage span.private_repo_msg {
3429 font-size: 0.8em;
3437 font-size: 0.8em;
3430 opacity: 0.6px;
3438 opacity: 0.6px;
3431 }
3439 }
3432
3440
3433 table#permissions_manage td.private_repo_msg {
3441 table#permissions_manage td.private_repo_msg {
3434 font-size: 0.8em;
3442 font-size: 0.8em;
3435 }
3443 }
3436
3444
3437 table#permissions_manage tr#add_perm_input td {
3445 table#permissions_manage tr#add_perm_input td {
3438 vertical-align: middle;
3446 vertical-align: middle;
3439 }
3447 }
3440
3448
3441 div.gravatar {
3449 div.gravatar {
3442 background-color: #FFF;
3450 background-color: #FFF;
3443 float: left;
3451 float: left;
3444 margin-right: 0.7em;
3452 margin-right: 0.7em;
3445 padding: 1px 1px 1px 1px;
3453 padding: 1px 1px 1px 1px;
3446 line-height:0;
3454 line-height:0;
3447 -webkit-border-radius: 3px;
3455 -webkit-border-radius: 3px;
3448 -khtml-border-radius: 3px;
3456 -khtml-border-radius: 3px;
3449 -moz-border-radius: 3px;
3457 -moz-border-radius: 3px;
3450 border-radius: 3px;
3458 border-radius: 3px;
3451 }
3459 }
3452
3460
3453 div.gravatar img {
3461 div.gravatar img {
3454 -webkit-border-radius: 2px;
3462 -webkit-border-radius: 2px;
3455 -khtml-border-radius: 2px;
3463 -khtml-border-radius: 2px;
3456 -moz-border-radius: 2px;
3464 -moz-border-radius: 2px;
3457 border-radius: 2px;
3465 border-radius: 2px;
3458 }
3466 }
3459
3467
3460 #header,#content,#footer {
3468 #header,#content,#footer {
3461 min-width: 978px;
3469 min-width: 978px;
3462 }
3470 }
3463
3471
3464 #content {
3472 #content {
3465 clear: both;
3473 clear: both;
3466 overflow: hidden;
3474 overflow: hidden;
3467 padding: 54px 10px 14px 10px;
3475 padding: 54px 10px 14px 10px;
3468 }
3476 }
3469
3477
3470 #content div.box div.title div.search {
3478 #content div.box div.title div.search {
3471
3479
3472 border-left: 1px solid #316293;
3480 border-left: 1px solid #316293;
3473 }
3481 }
3474
3482
3475 #content div.box div.title div.search div.input input {
3483 #content div.box div.title div.search div.input input {
3476 border: 1px solid #316293;
3484 border: 1px solid #316293;
3477 }
3485 }
3478
3486
3479 .ui-btn{
3487 .ui-btn{
3480 color: #515151;
3488 color: #515151;
3481 background-color: #DADADA;
3489 background-color: #DADADA;
3482 background-repeat: repeat-x;
3490 background-repeat: repeat-x;
3483 background-image: -khtml-gradient(linear, left top, left bottom, from(#F4F4F4),to(#DADADA) );
3491 background-image: -khtml-gradient(linear, left top, left bottom, from(#F4F4F4),to(#DADADA) );
3484 background-image: -moz-linear-gradient(top, #F4F4F4, #DADADA);
3492 background-image: -moz-linear-gradient(top, #F4F4F4, #DADADA);
3485 background-image: -ms-linear-gradient(top, #F4F4F4, #DADADA);
3493 background-image: -ms-linear-gradient(top, #F4F4F4, #DADADA);
3486 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #F4F4F4),color-stop(100%, #DADADA) );
3494 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #F4F4F4),color-stop(100%, #DADADA) );
3487 background-image: -webkit-linear-gradient(top, #F4F4F4, #DADADA) );
3495 background-image: -webkit-linear-gradient(top, #F4F4F4, #DADADA) );
3488 background-image: -o-linear-gradient(top, #F4F4F4, #DADADA) );
3496 background-image: -o-linear-gradient(top, #F4F4F4, #DADADA) );
3489 background-image: linear-gradient(top, #F4F4F4, #DADADA);
3497 background-image: linear-gradient(top, #F4F4F4, #DADADA);
3490 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#F4F4F4', endColorstr='#DADADA', GradientType=0);
3498 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#F4F4F4', endColorstr='#DADADA', GradientType=0);
3491
3499
3492 border-top: 1px solid #DDD;
3500 border-top: 1px solid #DDD;
3493 border-left: 1px solid #c6c6c6;
3501 border-left: 1px solid #c6c6c6;
3494 border-right: 1px solid #DDD;
3502 border-right: 1px solid #DDD;
3495 border-bottom: 1px solid #c6c6c6;
3503 border-bottom: 1px solid #c6c6c6;
3496 color: #515151;
3504 color: #515151;
3497 outline: none;
3505 outline: none;
3498 margin: 0px 3px 3px 0px;
3506 margin: 0px 3px 3px 0px;
3499 -webkit-border-radius: 4px 4px 4px 4px !important;
3507 -webkit-border-radius: 4px 4px 4px 4px !important;
3500 -khtml-border-radius: 4px 4px 4px 4px !important;
3508 -khtml-border-radius: 4px 4px 4px 4px !important;
3501 -moz-border-radius: 4px 4px 4px 4px !important;
3509 -moz-border-radius: 4px 4px 4px 4px !important;
3502 border-radius: 4px 4px 4px 4px !important;
3510 border-radius: 4px 4px 4px 4px !important;
3503 cursor: pointer !important;
3511 cursor: pointer !important;
3504 padding: 3px 3px 3px 3px;
3512 padding: 3px 3px 3px 3px;
3505 background-position: 0 -15px;
3513 background-position: 0 -15px;
3506
3514
3507 }
3515 }
3508 .ui-btn.xsmall{
3516 .ui-btn.xsmall{
3509 padding: 1px 2px 1px 1px;
3517 padding: 1px 2px 1px 1px;
3510 }
3518 }
3511
3519
3512 .ui-btn.large{
3520 .ui-btn.large{
3513 padding: 6px 12px;
3521 padding: 6px 12px;
3514 }
3522 }
3515
3523
3516 .ui-btn.clone{
3524 .ui-btn.clone{
3517 padding: 5px 2px 6px 1px;
3525 padding: 5px 2px 6px 1px;
3518 margin: 0px -4px 3px 0px;
3526 margin: 0px -4px 3px 0px;
3519 -webkit-border-radius: 4px 0px 0px 4px !important;
3527 -webkit-border-radius: 4px 0px 0px 4px !important;
3520 -khtml-border-radius: 4px 0px 0px 4px !important;
3528 -khtml-border-radius: 4px 0px 0px 4px !important;
3521 -moz-border-radius: 4px 0px 0px 4px !important;
3529 -moz-border-radius: 4px 0px 0px 4px !important;
3522 border-radius: 4px 0px 0px 4px !important;
3530 border-radius: 4px 0px 0px 4px !important;
3523 width: 100px;
3531 width: 100px;
3524 text-align: center;
3532 text-align: center;
3525 float: left;
3533 float: left;
3526 position: absolute;
3534 position: absolute;
3527 }
3535 }
3528 .ui-btn:focus {
3536 .ui-btn:focus {
3529 outline: none;
3537 outline: none;
3530 }
3538 }
3531 .ui-btn:hover{
3539 .ui-btn:hover{
3532 background-position: 0 0px;
3540 background-position: 0 0px;
3533 text-decoration: none;
3541 text-decoration: none;
3534 color: #515151;
3542 color: #515151;
3535 box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25), 0 0 3px #FFFFFF !important;
3543 box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25), 0 0 3px #FFFFFF !important;
3536 }
3544 }
3537
3545
3538 .ui-btn.red{
3546 .ui-btn.red{
3539 color:#fff;
3547 color:#fff;
3540 background-color: #c43c35;
3548 background-color: #c43c35;
3541 background-repeat: repeat-x;
3549 background-repeat: repeat-x;
3542 background-image: -khtml-gradient(linear, left top, left bottom, from(#ee5f5b), to(#c43c35));
3550 background-image: -khtml-gradient(linear, left top, left bottom, from(#ee5f5b), to(#c43c35));
3543 background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35);
3551 background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35);
3544 background-image: -ms-linear-gradient(top, #ee5f5b, #c43c35);
3552 background-image: -ms-linear-gradient(top, #ee5f5b, #c43c35);
3545 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ee5f5b), color-stop(100%, #c43c35));
3553 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ee5f5b), color-stop(100%, #c43c35));
3546 background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35);
3554 background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35);
3547 background-image: -o-linear-gradient(top, #ee5f5b, #c43c35);
3555 background-image: -o-linear-gradient(top, #ee5f5b, #c43c35);
3548 background-image: linear-gradient(top, #ee5f5b, #c43c35);
3556 background-image: linear-gradient(top, #ee5f5b, #c43c35);
3549 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0);
3557 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0);
3550 border-color: #c43c35 #c43c35 #882a25;
3558 border-color: #c43c35 #c43c35 #882a25;
3551 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
3559 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
3552 }
3560 }
3553
3561
3554
3562
3555 .ui-btn.blue{
3563 .ui-btn.blue{
3556 color:#fff;
3564 color:#fff;
3557 background-color: #339bb9;
3565 background-color: #339bb9;
3558 background-repeat: repeat-x;
3566 background-repeat: repeat-x;
3559 background-image: -khtml-gradient(linear, left top, left bottom, from(#5bc0de), to(#339bb9));
3567 background-image: -khtml-gradient(linear, left top, left bottom, from(#5bc0de), to(#339bb9));
3560 background-image: -moz-linear-gradient(top, #5bc0de, #339bb9);
3568 background-image: -moz-linear-gradient(top, #5bc0de, #339bb9);
3561 background-image: -ms-linear-gradient(top, #5bc0de, #339bb9);
3569 background-image: -ms-linear-gradient(top, #5bc0de, #339bb9);
3562 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #5bc0de), color-stop(100%, #339bb9));
3570 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #5bc0de), color-stop(100%, #339bb9));
3563 background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9);
3571 background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9);
3564 background-image: -o-linear-gradient(top, #5bc0de, #339bb9);
3572 background-image: -o-linear-gradient(top, #5bc0de, #339bb9);
3565 background-image: linear-gradient(top, #5bc0de, #339bb9);
3573 background-image: linear-gradient(top, #5bc0de, #339bb9);
3566 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0);
3574 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0);
3567 border-color: #339bb9 #339bb9 #22697d;
3575 border-color: #339bb9 #339bb9 #22697d;
3568 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
3576 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
3569 }
3577 }
3570
3578
3571 .ui-btn.green{
3579 .ui-btn.green{
3572 background-color: #57a957;
3580 background-color: #57a957;
3573 background-repeat: repeat-x;
3581 background-repeat: repeat-x;
3574 background-image: -khtml-gradient(linear, left top, left bottom, from(#62c462), to(#57a957));
3582 background-image: -khtml-gradient(linear, left top, left bottom, from(#62c462), to(#57a957));
3575 background-image: -moz-linear-gradient(top, #62c462, #57a957);
3583 background-image: -moz-linear-gradient(top, #62c462, #57a957);
3576 background-image: -ms-linear-gradient(top, #62c462, #57a957);
3584 background-image: -ms-linear-gradient(top, #62c462, #57a957);
3577 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #62c462), color-stop(100%, #57a957));
3585 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #62c462), color-stop(100%, #57a957));
3578 background-image: -webkit-linear-gradient(top, #62c462, #57a957);
3586 background-image: -webkit-linear-gradient(top, #62c462, #57a957);
3579 background-image: -o-linear-gradient(top, #62c462, #57a957);
3587 background-image: -o-linear-gradient(top, #62c462, #57a957);
3580 background-image: linear-gradient(top, #62c462, #57a957);
3588 background-image: linear-gradient(top, #62c462, #57a957);
3581 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0);
3589 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0);
3582 border-color: #57a957 #57a957 #3d773d;
3590 border-color: #57a957 #57a957 #3d773d;
3583 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
3591 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
3584 }
3592 }
3585
3593
3586 .ui-btn.active{
3594 .ui-btn.active{
3587 font-weight: bold;
3595 font-weight: bold;
3588 }
3596 }
3589
3597
3590 ins,div.options a:hover {
3598 ins,div.options a:hover {
3591 text-decoration: none;
3599 text-decoration: none;
3592 }
3600 }
3593
3601
3594 img,
3602 img,
3595 #header #header-inner #quick li a:hover span.normal,
3603 #header #header-inner #quick li a:hover span.normal,
3596 #header #header-inner #quick li ul li.last,
3604 #header #header-inner #quick li ul li.last,
3597 #content div.box div.form div.fields div.field div.textarea table td table td a,
3605 #content div.box div.form div.fields div.field div.textarea table td table td a,
3598 #clone_url,
3606 #clone_url,
3599 #clone_url_id
3607 #clone_url_id
3600 {
3608 {
3601 border: none;
3609 border: none;
3602 }
3610 }
3603
3611
3604 img.icon,.right .merge img {
3612 img.icon,.right .merge img {
3605 vertical-align: bottom;
3613 vertical-align: bottom;
3606 }
3614 }
3607
3615
3608 #header ul#logged-user,#content div.box div.title ul.links,
3616 #header ul#logged-user,#content div.box div.title ul.links,
3609 #content div.box div.message div.dismiss,
3617 #content div.box div.message div.dismiss,
3610 #content div.box div.traffic div.legend ul
3618 #content div.box div.traffic div.legend ul
3611 {
3619 {
3612 float: right;
3620 float: right;
3613 margin: 0;
3621 margin: 0;
3614 padding: 0;
3622 padding: 0;
3615 }
3623 }
3616
3624
3617 #header #header-inner #home,#header #header-inner #logo,
3625 #header #header-inner #home,#header #header-inner #logo,
3618 #content div.box ul.left,#content div.box ol.left,
3626 #content div.box ul.left,#content div.box ol.left,
3619 #content div.box div.pagination-left,div#commit_history,
3627 #content div.box div.pagination-left,div#commit_history,
3620 div#legend_data,div#legend_container,div#legend_choices
3628 div#legend_data,div#legend_container,div#legend_choices
3621 {
3629 {
3622 float: left;
3630 float: left;
3623 }
3631 }
3624
3632
3625 #header #header-inner #quick li:hover ul ul,
3633 #header #header-inner #quick li:hover ul ul,
3626 #header #header-inner #quick li:hover ul ul ul,
3634 #header #header-inner #quick li:hover ul ul ul,
3627 #header #header-inner #quick li:hover ul ul ul ul,
3635 #header #header-inner #quick li:hover ul ul ul ul,
3628 #content #left #menu ul.closed,#content #left #menu li ul.collapsed,.yui-tt-shadow
3636 #content #left #menu ul.closed,#content #left #menu li ul.collapsed,.yui-tt-shadow
3629 {
3637 {
3630 display: none;
3638 display: none;
3631 }
3639 }
3632
3640
3633 #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
3641 #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
3634 {
3642 {
3635 display: block;
3643 display: block;
3636 }
3644 }
3637
3645
3638 #content div.graph {
3646 #content div.graph {
3639 padding: 0 10px 10px;
3647 padding: 0 10px 10px;
3640 }
3648 }
3641
3649
3642 #content div.box div.title ul.links li a:hover,#content div.box div.title ul.links li.ui-tabs-selected a
3650 #content div.box div.title ul.links li a:hover,#content div.box div.title ul.links li.ui-tabs-selected a
3643 {
3651 {
3644 color: #bfe3ff;
3652 color: #bfe3ff;
3645 }
3653 }
3646
3654
3647 #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
3655 #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
3648 {
3656 {
3649 margin: 10px 24px 10px 44px;
3657 margin: 10px 24px 10px 44px;
3650 }
3658 }
3651
3659
3652 #content div.box div.form,#content div.box div.table,#content div.box div.traffic
3660 #content div.box div.form,#content div.box div.table,#content div.box div.traffic
3653 {
3661 {
3654 clear: both;
3662 clear: both;
3655 overflow: hidden;
3663 overflow: hidden;
3656 margin: 0;
3664 margin: 0;
3657 padding: 0 20px 10px;
3665 padding: 0 20px 10px;
3658 }
3666 }
3659
3667
3660 #content div.box div.form div.fields,#login div.form,#login div.form div.fields,#register div.form,#register div.form div.fields
3668 #content div.box div.form div.fields,#login div.form,#login div.form div.fields,#register div.form,#register div.form div.fields
3661 {
3669 {
3662 clear: both;
3670 clear: both;
3663 overflow: hidden;
3671 overflow: hidden;
3664 margin: 0;
3672 margin: 0;
3665 padding: 0;
3673 padding: 0;
3666 }
3674 }
3667
3675
3668 #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
3676 #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
3669 {
3677 {
3670 height: 1%;
3678 height: 1%;
3671 display: block;
3679 display: block;
3672 color: #363636;
3680 color: #363636;
3673 margin: 0;
3681 margin: 0;
3674 padding: 2px 0 0;
3682 padding: 2px 0 0;
3675 }
3683 }
3676
3684
3677 #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
3685 #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
3678 {
3686 {
3679 background: #FBE3E4;
3687 background: #FBE3E4;
3680 border-top: 1px solid #e1b2b3;
3688 border-top: 1px solid #e1b2b3;
3681 border-left: 1px solid #e1b2b3;
3689 border-left: 1px solid #e1b2b3;
3682 border-right: 1px solid #FBC2C4;
3690 border-right: 1px solid #FBC2C4;
3683 border-bottom: 1px solid #FBC2C4;
3691 border-bottom: 1px solid #FBC2C4;
3684 }
3692 }
3685
3693
3686 #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
3694 #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
3687 {
3695 {
3688 background: #E6EFC2;
3696 background: #E6EFC2;
3689 border-top: 1px solid #cebb98;
3697 border-top: 1px solid #cebb98;
3690 border-left: 1px solid #cebb98;
3698 border-left: 1px solid #cebb98;
3691 border-right: 1px solid #c6d880;
3699 border-right: 1px solid #c6d880;
3692 border-bottom: 1px solid #c6d880;
3700 border-bottom: 1px solid #c6d880;
3693 }
3701 }
3694
3702
3695 #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
3703 #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
3696 {
3704 {
3697 margin: 0;
3705 margin: 0;
3698 }
3706 }
3699
3707
3700 #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
3708 #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
3701 {
3709 {
3702 margin: 0 0 0 0px !important;
3710 margin: 0 0 0 0px !important;
3703 padding: 0;
3711 padding: 0;
3704 }
3712 }
3705
3713
3706 #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
3714 #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
3707 {
3715 {
3708 margin: 0 0 0 200px;
3716 margin: 0 0 0 200px;
3709 padding: 0;
3717 padding: 0;
3710 }
3718 }
3711
3719
3712 #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
3720 #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
3713 {
3721 {
3714 color: #000;
3722 color: #000;
3715 text-decoration: none;
3723 text-decoration: none;
3716 }
3724 }
3717
3725
3718 #content div.box div.form div.fields div.field div.select a.ui-selectmenu-focus,#content div.box div.action a.ui-selectmenu-focus
3726 #content div.box div.form div.fields div.field div.select a.ui-selectmenu-focus,#content div.box div.action a.ui-selectmenu-focus
3719 {
3727 {
3720 border: 1px solid #666;
3728 border: 1px solid #666;
3721 }
3729 }
3722
3730
3723 #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
3731 #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
3724 {
3732 {
3725 clear: both;
3733 clear: both;
3726 overflow: hidden;
3734 overflow: hidden;
3727 margin: 0;
3735 margin: 0;
3728 padding: 8px 0 2px;
3736 padding: 8px 0 2px;
3729 }
3737 }
3730
3738
3731 #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
3739 #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
3732 {
3740 {
3733 float: left;
3741 float: left;
3734 margin: 0;
3742 margin: 0;
3735 }
3743 }
3736
3744
3737 #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
3745 #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
3738 {
3746 {
3739 height: 1%;
3747 height: 1%;
3740 display: block;
3748 display: block;
3741 float: left;
3749 float: left;
3742 margin: 2px 0 0 4px;
3750 margin: 2px 0 0 4px;
3743 }
3751 }
3744
3752
3745 div.form div.fields div.field div.button input,
3753 div.form div.fields div.field div.button input,
3746 #content div.box div.form div.fields div.buttons input
3754 #content div.box div.form div.fields div.buttons input
3747 div.form div.fields div.buttons input,
3755 div.form div.fields div.buttons input,
3748 #content div.box div.action div.button input {
3756 #content div.box div.action div.button input {
3749 /*color: #000;*/
3757 /*color: #000;*/
3750 font-size: 11px;
3758 font-size: 11px;
3751 font-weight: 700;
3759 font-weight: 700;
3752 margin: 0;
3760 margin: 0;
3753 }
3761 }
3754
3762
3755 input.ui-button {
3763 input.ui-button {
3756 background: #e5e3e3 url("../images/button.png") repeat-x;
3764 background: #e5e3e3 url("../images/button.png") repeat-x;
3757 border-top: 1px solid #DDD;
3765 border-top: 1px solid #DDD;
3758 border-left: 1px solid #c6c6c6;
3766 border-left: 1px solid #c6c6c6;
3759 border-right: 1px solid #DDD;
3767 border-right: 1px solid #DDD;
3760 border-bottom: 1px solid #c6c6c6;
3768 border-bottom: 1px solid #c6c6c6;
3761 color: #515151 !important;
3769 color: #515151 !important;
3762 outline: none;
3770 outline: none;
3763 margin: 0;
3771 margin: 0;
3764 padding: 6px 12px;
3772 padding: 6px 12px;
3765 -webkit-border-radius: 4px 4px 4px 4px;
3773 -webkit-border-radius: 4px 4px 4px 4px;
3766 -khtml-border-radius: 4px 4px 4px 4px;
3774 -khtml-border-radius: 4px 4px 4px 4px;
3767 -moz-border-radius: 4px 4px 4px 4px;
3775 -moz-border-radius: 4px 4px 4px 4px;
3768 border-radius: 4px 4px 4px 4px;
3776 border-radius: 4px 4px 4px 4px;
3769 box-shadow: 0 1px 0 #ececec;
3777 box-shadow: 0 1px 0 #ececec;
3770 cursor: pointer;
3778 cursor: pointer;
3771 }
3779 }
3772
3780
3773 input.ui-button:hover {
3781 input.ui-button:hover {
3774 background: #b4b4b4 url("../images/button_selected.png") repeat-x;
3782 background: #b4b4b4 url("../images/button_selected.png") repeat-x;
3775 border-top: 1px solid #ccc;
3783 border-top: 1px solid #ccc;
3776 border-left: 1px solid #bebebe;
3784 border-left: 1px solid #bebebe;
3777 border-right: 1px solid #b1b1b1;
3785 border-right: 1px solid #b1b1b1;
3778 border-bottom: 1px solid #afafaf;
3786 border-bottom: 1px solid #afafaf;
3779 }
3787 }
3780
3788
3781 div.form div.fields div.field div.highlight,#content div.box div.form div.fields div.buttons div.highlight
3789 div.form div.fields div.field div.highlight,#content div.box div.form div.fields div.buttons div.highlight
3782 {
3790 {
3783 display: inline;
3791 display: inline;
3784 }
3792 }
3785
3793
3786 #content div.box div.form div.fields div.buttons,div.form div.fields div.buttons
3794 #content div.box div.form div.fields div.buttons,div.form div.fields div.buttons
3787 {
3795 {
3788 margin: 10px 0 0 200px;
3796 margin: 10px 0 0 200px;
3789 padding: 0;
3797 padding: 0;
3790 }
3798 }
3791
3799
3792 #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
3800 #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
3793 {
3801 {
3794 margin: 10px 0 0;
3802 margin: 10px 0 0;
3795 }
3803 }
3796
3804
3797 #content div.box table td.user,#content div.box table td.address {
3805 #content div.box table td.user,#content div.box table td.address {
3798 width: 10%;
3806 width: 10%;
3799 text-align: center;
3807 text-align: center;
3800 }
3808 }
3801
3809
3802 #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
3810 #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
3803 {
3811 {
3804 text-align: right;
3812 text-align: right;
3805 margin: 6px 0 0;
3813 margin: 6px 0 0;
3806 padding: 0;
3814 padding: 0;
3807 }
3815 }
3808
3816
3809 #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
3817 #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
3810 {
3818 {
3811 background: #b4b4b4 url("../images/button_selected.png") repeat-x;
3819 background: #b4b4b4 url("../images/button_selected.png") repeat-x;
3812 border-top: 1px solid #ccc;
3820 border-top: 1px solid #ccc;
3813 border-left: 1px solid #bebebe;
3821 border-left: 1px solid #bebebe;
3814 border-right: 1px solid #b1b1b1;
3822 border-right: 1px solid #b1b1b1;
3815 border-bottom: 1px solid #afafaf;
3823 border-bottom: 1px solid #afafaf;
3816 color: #515151;
3824 color: #515151;
3817 margin: 0;
3825 margin: 0;
3818 padding: 6px 12px;
3826 padding: 6px 12px;
3819 }
3827 }
3820
3828
3821 #content div.box div.pagination div.results,#content div.box div.pagination-wh div.results
3829 #content div.box div.pagination div.results,#content div.box div.pagination-wh div.results
3822 {
3830 {
3823 text-align: left;
3831 text-align: left;
3824 float: left;
3832 float: left;
3825 margin: 0;
3833 margin: 0;
3826 padding: 0;
3834 padding: 0;
3827 }
3835 }
3828
3836
3829 #content div.box div.pagination div.results span,#content div.box div.pagination-wh div.results span
3837 #content div.box div.pagination div.results span,#content div.box div.pagination-wh div.results span
3830 {
3838 {
3831 height: 1%;
3839 height: 1%;
3832 display: block;
3840 display: block;
3833 float: left;
3841 float: left;
3834 background: #ebebeb url("../images/pager.png") repeat-x;
3842 background: #ebebeb url("../images/pager.png") repeat-x;
3835 border-top: 1px solid #dedede;
3843 border-top: 1px solid #dedede;
3836 border-left: 1px solid #cfcfcf;
3844 border-left: 1px solid #cfcfcf;
3837 border-right: 1px solid #c4c4c4;
3845 border-right: 1px solid #c4c4c4;
3838 border-bottom: 1px solid #c4c4c4;
3846 border-bottom: 1px solid #c4c4c4;
3839 color: #4A4A4A;
3847 color: #4A4A4A;
3840 font-weight: 700;
3848 font-weight: 700;
3841 margin: 0;
3849 margin: 0;
3842 padding: 6px 8px;
3850 padding: 6px 8px;
3843 }
3851 }
3844
3852
3845 #content div.box div.pagination ul.pager li.disabled,#content div.box div.pagination-wh a.disabled
3853 #content div.box div.pagination ul.pager li.disabled,#content div.box div.pagination-wh a.disabled
3846 {
3854 {
3847 color: #B4B4B4;
3855 color: #B4B4B4;
3848 padding: 6px;
3856 padding: 6px;
3849 }
3857 }
3850
3858
3851 #login,#register {
3859 #login,#register {
3852 width: 520px;
3860 width: 520px;
3853 margin: 10% auto 0;
3861 margin: 10% auto 0;
3854 padding: 0;
3862 padding: 0;
3855 }
3863 }
3856
3864
3857 #login div.color,#register div.color {
3865 #login div.color,#register div.color {
3858 clear: both;
3866 clear: both;
3859 overflow: hidden;
3867 overflow: hidden;
3860 background: #FFF;
3868 background: #FFF;
3861 margin: 10px auto 0;
3869 margin: 10px auto 0;
3862 padding: 3px 3px 3px 0;
3870 padding: 3px 3px 3px 0;
3863 }
3871 }
3864
3872
3865 #login div.color a,#register div.color a {
3873 #login div.color a,#register div.color a {
3866 width: 20px;
3874 width: 20px;
3867 height: 20px;
3875 height: 20px;
3868 display: block;
3876 display: block;
3869 float: left;
3877 float: left;
3870 margin: 0 0 0 3px;
3878 margin: 0 0 0 3px;
3871 padding: 0;
3879 padding: 0;
3872 }
3880 }
3873
3881
3874 #login div.title h5,#register div.title h5 {
3882 #login div.title h5,#register div.title h5 {
3875 color: #fff;
3883 color: #fff;
3876 margin: 10px;
3884 margin: 10px;
3877 padding: 0;
3885 padding: 0;
3878 }
3886 }
3879
3887
3880 #login div.form div.fields div.field,#register div.form div.fields div.field
3888 #login div.form div.fields div.field,#register div.form div.fields div.field
3881 {
3889 {
3882 clear: both;
3890 clear: both;
3883 overflow: hidden;
3891 overflow: hidden;
3884 margin: 0;
3892 margin: 0;
3885 padding: 0 0 10px;
3893 padding: 0 0 10px;
3886 }
3894 }
3887
3895
3888 #login div.form div.fields div.field span.error-message,#register div.form div.fields div.field span.error-message
3896 #login div.form div.fields div.field span.error-message,#register div.form div.fields div.field span.error-message
3889 {
3897 {
3890 height: 1%;
3898 height: 1%;
3891 display: block;
3899 display: block;
3892 color: red;
3900 color: red;
3893 margin: 8px 0 0;
3901 margin: 8px 0 0;
3894 padding: 0;
3902 padding: 0;
3895 max-width: 320px;
3903 max-width: 320px;
3896 }
3904 }
3897
3905
3898 #login div.form div.fields div.field div.label label,#register div.form div.fields div.field div.label label
3906 #login div.form div.fields div.field div.label label,#register div.form div.fields div.field div.label label
3899 {
3907 {
3900 color: #000;
3908 color: #000;
3901 font-weight: 700;
3909 font-weight: 700;
3902 }
3910 }
3903
3911
3904 #login div.form div.fields div.field div.input,#register div.form div.fields div.field div.input
3912 #login div.form div.fields div.field div.input,#register div.form div.fields div.field div.input
3905 {
3913 {
3906 float: left;
3914 float: left;
3907 margin: 0;
3915 margin: 0;
3908 padding: 0;
3916 padding: 0;
3909 }
3917 }
3910
3918
3911 #login div.form div.fields div.field div.checkbox,#register div.form div.fields div.field div.checkbox
3919 #login div.form div.fields div.field div.checkbox,#register div.form div.fields div.field div.checkbox
3912 {
3920 {
3913 margin: 0 0 0 184px;
3921 margin: 0 0 0 184px;
3914 padding: 0;
3922 padding: 0;
3915 }
3923 }
3916
3924
3917 #login div.form div.fields div.field div.checkbox label,#register div.form div.fields div.field div.checkbox label
3925 #login div.form div.fields div.field div.checkbox label,#register div.form div.fields div.field div.checkbox label
3918 {
3926 {
3919 color: #565656;
3927 color: #565656;
3920 font-weight: 700;
3928 font-weight: 700;
3921 }
3929 }
3922
3930
3923 #login div.form div.fields div.buttons input,#register div.form div.fields div.buttons input
3931 #login div.form div.fields div.buttons input,#register div.form div.fields div.buttons input
3924 {
3932 {
3925 color: #000;
3933 color: #000;
3926 font-size: 1em;
3934 font-size: 1em;
3927 font-weight: 700;
3935 font-weight: 700;
3928 margin: 0;
3936 margin: 0;
3929 }
3937 }
3930
3938
3931 #changeset_content .container .wrapper,#graph_content .container .wrapper
3939 #changeset_content .container .wrapper,#graph_content .container .wrapper
3932 {
3940 {
3933 width: 600px;
3941 width: 600px;
3934 }
3942 }
3935
3943
3936 #changeset_content .container .left {
3944 #changeset_content .container .left {
3937 float: left;
3945 float: left;
3938 width: 75%;
3946 width: 75%;
3939 padding-left: 5px;
3947 padding-left: 5px;
3940 }
3948 }
3941
3949
3942 #changeset_content .container .left .date,.ac .match {
3950 #changeset_content .container .left .date,.ac .match {
3943 font-weight: 700;
3951 font-weight: 700;
3944 padding-top: 5px;
3952 padding-top: 5px;
3945 padding-bottom: 5px;
3953 padding-bottom: 5px;
3946 }
3954 }
3947
3955
3948 div#legend_container table td,div#legend_choices table td {
3956 div#legend_container table td,div#legend_choices table td {
3949 border: none !important;
3957 border: none !important;
3950 height: 20px !important;
3958 height: 20px !important;
3951 padding: 0 !important;
3959 padding: 0 !important;
3952 }
3960 }
3953
3961
3954 .q_filter_box {
3962 .q_filter_box {
3955 -webkit-box-shadow: rgba(0,0,0,0.07) 0 1px 2px inset;
3963 -webkit-box-shadow: rgba(0,0,0,0.07) 0 1px 2px inset;
3956 -webkit-border-radius: 4px;
3964 -webkit-border-radius: 4px;
3957 -moz-border-radius: 4px;
3965 -moz-border-radius: 4px;
3958 border-radius: 4px;
3966 border-radius: 4px;
3959 border: 0 none;
3967 border: 0 none;
3960 color: #AAAAAA;
3968 color: #AAAAAA;
3961 margin-bottom: -4px;
3969 margin-bottom: -4px;
3962 margin-top: -4px;
3970 margin-top: -4px;
3963 padding-left: 3px;
3971 padding-left: 3px;
3964 }
3972 }
3965
3973
3966 #node_filter {
3974 #node_filter {
3967 border: 0px solid #545454;
3975 border: 0px solid #545454;
3968 color: #AAAAAA;
3976 color: #AAAAAA;
3969 padding-left: 3px;
3977 padding-left: 3px;
3970 }
3978 }
3971
3979
3972
3980
3973 .group_members_wrap{
3981 .group_members_wrap{
3974 min-height: 85px;
3982 min-height: 85px;
3975 padding-left: 20px;
3983 padding-left: 20px;
3976 }
3984 }
3977
3985
3978 .group_members .group_member{
3986 .group_members .group_member{
3979 height: 30px;
3987 height: 30px;
3980 padding:0px 0px 0px 0px;
3988 padding:0px 0px 0px 0px;
3981 }
3989 }
3982
3990
3983 .reviewers_member{
3991 .reviewers_member{
3984 height: 15px;
3992 height: 15px;
3985 padding:0px 0px 0px 10px;
3993 padding:0px 0px 0px 10px;
3986 }
3994 }
3987
3995
3988 .emails_wrap{
3996 .emails_wrap{
3989 padding: 0px 20px;
3997 padding: 0px 20px;
3990 }
3998 }
3991
3999
3992 .emails_wrap .email_entry{
4000 .emails_wrap .email_entry{
3993 height: 30px;
4001 height: 30px;
3994 padding:0px 0px 0px 10px;
4002 padding:0px 0px 0px 10px;
3995 }
4003 }
3996 .emails_wrap .email_entry .email{
4004 .emails_wrap .email_entry .email{
3997 float: left
4005 float: left
3998 }
4006 }
3999 .emails_wrap .email_entry .email_action{
4007 .emails_wrap .email_entry .email_action{
4000 float: left
4008 float: left
4001 }
4009 }
4002
4010
4003 /*README STYLE*/
4011 /*README STYLE*/
4004
4012
4005 div.readme {
4013 div.readme {
4006 padding:0px;
4014 padding:0px;
4007 }
4015 }
4008
4016
4009 div.readme h2 {
4017 div.readme h2 {
4010 font-weight: normal;
4018 font-weight: normal;
4011 }
4019 }
4012
4020
4013 div.readme .readme_box {
4021 div.readme .readme_box {
4014 background-color: #fafafa;
4022 background-color: #fafafa;
4015 }
4023 }
4016
4024
4017 div.readme .readme_box {
4025 div.readme .readme_box {
4018 clear:both;
4026 clear:both;
4019 overflow:hidden;
4027 overflow:hidden;
4020 margin:0;
4028 margin:0;
4021 padding:0 20px 10px;
4029 padding:0 20px 10px;
4022 }
4030 }
4023
4031
4024 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 {
4032 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 {
4025 border-bottom: 0 !important;
4033 border-bottom: 0 !important;
4026 margin: 0 !important;
4034 margin: 0 !important;
4027 padding: 0 !important;
4035 padding: 0 !important;
4028 line-height: 1.5em !important;
4036 line-height: 1.5em !important;
4029 }
4037 }
4030
4038
4031
4039
4032 div.readme .readme_box h1:first-child {
4040 div.readme .readme_box h1:first-child {
4033 padding-top: .25em !important;
4041 padding-top: .25em !important;
4034 }
4042 }
4035
4043
4036 div.readme .readme_box h2, div.readme .readme_box h3 {
4044 div.readme .readme_box h2, div.readme .readme_box h3 {
4037 margin: 1em 0 !important;
4045 margin: 1em 0 !important;
4038 }
4046 }
4039
4047
4040 div.readme .readme_box h2 {
4048 div.readme .readme_box h2 {
4041 margin-top: 1.5em !important;
4049 margin-top: 1.5em !important;
4042 border-top: 4px solid #e0e0e0 !important;
4050 border-top: 4px solid #e0e0e0 !important;
4043 padding-top: .5em !important;
4051 padding-top: .5em !important;
4044 }
4052 }
4045
4053
4046 div.readme .readme_box p {
4054 div.readme .readme_box p {
4047 color: black !important;
4055 color: black !important;
4048 margin: 1em 0 !important;
4056 margin: 1em 0 !important;
4049 line-height: 1.5em !important;
4057 line-height: 1.5em !important;
4050 }
4058 }
4051
4059
4052 div.readme .readme_box ul {
4060 div.readme .readme_box ul {
4053 list-style: disc !important;
4061 list-style: disc !important;
4054 margin: 1em 0 1em 2em !important;
4062 margin: 1em 0 1em 2em !important;
4055 }
4063 }
4056
4064
4057 div.readme .readme_box ol {
4065 div.readme .readme_box ol {
4058 list-style: decimal;
4066 list-style: decimal;
4059 margin: 1em 0 1em 2em !important;
4067 margin: 1em 0 1em 2em !important;
4060 }
4068 }
4061
4069
4062 div.readme .readme_box pre, code {
4070 div.readme .readme_box pre, code {
4063 font: 12px "Bitstream Vera Sans Mono","Courier",monospace;
4071 font: 12px "Bitstream Vera Sans Mono","Courier",monospace;
4064 }
4072 }
4065
4073
4066 div.readme .readme_box code {
4074 div.readme .readme_box code {
4067 font-size: 12px !important;
4075 font-size: 12px !important;
4068 background-color: ghostWhite !important;
4076 background-color: ghostWhite !important;
4069 color: #444 !important;
4077 color: #444 !important;
4070 padding: 0 .2em !important;
4078 padding: 0 .2em !important;
4071 border: 1px solid #dedede !important;
4079 border: 1px solid #dedede !important;
4072 }
4080 }
4073
4081
4074 div.readme .readme_box pre code {
4082 div.readme .readme_box pre code {
4075 padding: 0 !important;
4083 padding: 0 !important;
4076 font-size: 12px !important;
4084 font-size: 12px !important;
4077 background-color: #eee !important;
4085 background-color: #eee !important;
4078 border: none !important;
4086 border: none !important;
4079 }
4087 }
4080
4088
4081 div.readme .readme_box pre {
4089 div.readme .readme_box pre {
4082 margin: 1em 0;
4090 margin: 1em 0;
4083 font-size: 12px;
4091 font-size: 12px;
4084 background-color: #eee;
4092 background-color: #eee;
4085 border: 1px solid #ddd;
4093 border: 1px solid #ddd;
4086 padding: 5px;
4094 padding: 5px;
4087 color: #444;
4095 color: #444;
4088 overflow: auto;
4096 overflow: auto;
4089 -webkit-box-shadow: rgba(0,0,0,0.07) 0 1px 2px inset;
4097 -webkit-box-shadow: rgba(0,0,0,0.07) 0 1px 2px inset;
4090 -webkit-border-radius: 3px;
4098 -webkit-border-radius: 3px;
4091 -moz-border-radius: 3px;
4099 -moz-border-radius: 3px;
4092 border-radius: 3px;
4100 border-radius: 3px;
4093 }
4101 }
4094
4102
4095 div.readme .readme_box table {
4103 div.readme .readme_box table {
4096 display: table;
4104 display: table;
4097 border-collapse: separate;
4105 border-collapse: separate;
4098 border-spacing: 2px;
4106 border-spacing: 2px;
4099 border-color: gray;
4107 border-color: gray;
4100 width: auto !important;
4108 width: auto !important;
4101 }
4109 }
4102
4110
4103
4111
4104 /** RST STYLE **/
4112 /** RST STYLE **/
4105
4113
4106
4114
4107 div.rst-block {
4115 div.rst-block {
4108 padding:0px;
4116 padding:0px;
4109 }
4117 }
4110
4118
4111 div.rst-block h2 {
4119 div.rst-block h2 {
4112 font-weight: normal;
4120 font-weight: normal;
4113 }
4121 }
4114
4122
4115 div.rst-block {
4123 div.rst-block {
4116 background-color: #fafafa;
4124 background-color: #fafafa;
4117 }
4125 }
4118
4126
4119 div.rst-block {
4127 div.rst-block {
4120 clear:both;
4128 clear:both;
4121 overflow:hidden;
4129 overflow:hidden;
4122 margin:0;
4130 margin:0;
4123 padding:0 20px 10px;
4131 padding:0 20px 10px;
4124 }
4132 }
4125
4133
4126 div.rst-block h1, div.rst-block h2, div.rst-block h3, div.rst-block h4, div.rst-block h5, div.rst-block h6 {
4134 div.rst-block h1, div.rst-block h2, div.rst-block h3, div.rst-block h4, div.rst-block h5, div.rst-block h6 {
4127 border-bottom: 0 !important;
4135 border-bottom: 0 !important;
4128 margin: 0 !important;
4136 margin: 0 !important;
4129 padding: 0 !important;
4137 padding: 0 !important;
4130 line-height: 1.5em !important;
4138 line-height: 1.5em !important;
4131 }
4139 }
4132
4140
4133
4141
4134 div.rst-block h1:first-child {
4142 div.rst-block h1:first-child {
4135 padding-top: .25em !important;
4143 padding-top: .25em !important;
4136 }
4144 }
4137
4145
4138 div.rst-block h2, div.rst-block h3 {
4146 div.rst-block h2, div.rst-block h3 {
4139 margin: 1em 0 !important;
4147 margin: 1em 0 !important;
4140 }
4148 }
4141
4149
4142 div.rst-block h2 {
4150 div.rst-block h2 {
4143 margin-top: 1.5em !important;
4151 margin-top: 1.5em !important;
4144 border-top: 4px solid #e0e0e0 !important;
4152 border-top: 4px solid #e0e0e0 !important;
4145 padding-top: .5em !important;
4153 padding-top: .5em !important;
4146 }
4154 }
4147
4155
4148 div.rst-block p {
4156 div.rst-block p {
4149 color: black !important;
4157 color: black !important;
4150 margin: 1em 0 !important;
4158 margin: 1em 0 !important;
4151 line-height: 1.5em !important;
4159 line-height: 1.5em !important;
4152 }
4160 }
4153
4161
4154 div.rst-block ul {
4162 div.rst-block ul {
4155 list-style: disc !important;
4163 list-style: disc !important;
4156 margin: 1em 0 1em 2em !important;
4164 margin: 1em 0 1em 2em !important;
4157 }
4165 }
4158
4166
4159 div.rst-block ol {
4167 div.rst-block ol {
4160 list-style: decimal;
4168 list-style: decimal;
4161 margin: 1em 0 1em 2em !important;
4169 margin: 1em 0 1em 2em !important;
4162 }
4170 }
4163
4171
4164 div.rst-block pre, code {
4172 div.rst-block pre, code {
4165 font: 12px "Bitstream Vera Sans Mono","Courier",monospace;
4173 font: 12px "Bitstream Vera Sans Mono","Courier",monospace;
4166 }
4174 }
4167
4175
4168 div.rst-block code {
4176 div.rst-block code {
4169 font-size: 12px !important;
4177 font-size: 12px !important;
4170 background-color: ghostWhite !important;
4178 background-color: ghostWhite !important;
4171 color: #444 !important;
4179 color: #444 !important;
4172 padding: 0 .2em !important;
4180 padding: 0 .2em !important;
4173 border: 1px solid #dedede !important;
4181 border: 1px solid #dedede !important;
4174 }
4182 }
4175
4183
4176 div.rst-block pre code {
4184 div.rst-block pre code {
4177 padding: 0 !important;
4185 padding: 0 !important;
4178 font-size: 12px !important;
4186 font-size: 12px !important;
4179 background-color: #eee !important;
4187 background-color: #eee !important;
4180 border: none !important;
4188 border: none !important;
4181 }
4189 }
4182
4190
4183 div.rst-block pre {
4191 div.rst-block pre {
4184 margin: 1em 0;
4192 margin: 1em 0;
4185 font-size: 12px;
4193 font-size: 12px;
4186 background-color: #eee;
4194 background-color: #eee;
4187 border: 1px solid #ddd;
4195 border: 1px solid #ddd;
4188 padding: 5px;
4196 padding: 5px;
4189 color: #444;
4197 color: #444;
4190 overflow: auto;
4198 overflow: auto;
4191 -webkit-box-shadow: rgba(0,0,0,0.07) 0 1px 2px inset;
4199 -webkit-box-shadow: rgba(0,0,0,0.07) 0 1px 2px inset;
4192 -webkit-border-radius: 3px;
4200 -webkit-border-radius: 3px;
4193 -moz-border-radius: 3px;
4201 -moz-border-radius: 3px;
4194 border-radius: 3px;
4202 border-radius: 3px;
4195 }
4203 }
4196
4204
4197
4205
4198 /** comment main **/
4206 /** comment main **/
4199 .comments {
4207 .comments {
4200 padding:10px 20px;
4208 padding:10px 20px;
4201 }
4209 }
4202
4210
4203 .comments .comment {
4211 .comments .comment {
4204 border: 1px solid #ddd;
4212 border: 1px solid #ddd;
4205 margin-top: 10px;
4213 margin-top: 10px;
4206 -webkit-border-radius: 4px;
4214 -webkit-border-radius: 4px;
4207 -moz-border-radius: 4px;
4215 -moz-border-radius: 4px;
4208 border-radius: 4px;
4216 border-radius: 4px;
4209 }
4217 }
4210
4218
4211 .comments .comment .meta {
4219 .comments .comment .meta {
4212 background: #f8f8f8;
4220 background: #f8f8f8;
4213 padding: 4px;
4221 padding: 4px;
4214 border-bottom: 1px solid #ddd;
4222 border-bottom: 1px solid #ddd;
4215 height: 18px;
4223 height: 18px;
4216 }
4224 }
4217
4225
4218 .comments .comment .meta img {
4226 .comments .comment .meta img {
4219 vertical-align: middle;
4227 vertical-align: middle;
4220 }
4228 }
4221
4229
4222 .comments .comment .meta .user {
4230 .comments .comment .meta .user {
4223 font-weight: bold;
4231 font-weight: bold;
4224 float: left;
4232 float: left;
4225 padding: 4px 2px 2px 2px;
4233 padding: 4px 2px 2px 2px;
4226 }
4234 }
4227
4235
4228 .comments .comment .meta .date {
4236 .comments .comment .meta .date {
4229 float: left;
4237 float: left;
4230 padding:4px 4px 0px 4px;
4238 padding:4px 4px 0px 4px;
4231 }
4239 }
4232
4240
4233 .comments .comment .text {
4241 .comments .comment .text {
4234 background-color: #FAFAFA;
4242 background-color: #FAFAFA;
4235 }
4243 }
4236 .comment .text div.rst-block p {
4244 .comment .text div.rst-block p {
4237 margin: 0.5em 0px !important;
4245 margin: 0.5em 0px !important;
4238 }
4246 }
4239
4247
4240 .comments .comments-number{
4248 .comments .comments-number{
4241 padding:0px 0px 10px 0px;
4249 padding:0px 0px 10px 0px;
4242 font-weight: bold;
4250 font-weight: bold;
4243 color: #666;
4251 color: #666;
4244 font-size: 16px;
4252 font-size: 16px;
4245 }
4253 }
4246
4254
4247 /** comment form **/
4255 /** comment form **/
4248
4256
4249 .status-block{
4257 .status-block{
4250 height:80px;
4258 height:80px;
4251 clear:both
4259 clear:both
4252 }
4260 }
4253
4261
4254 .comment-form .clearfix{
4262 .comment-form .clearfix{
4255 background: #EEE;
4263 background: #EEE;
4256 -webkit-border-radius: 4px;
4264 -webkit-border-radius: 4px;
4257 -moz-border-radius: 4px;
4265 -moz-border-radius: 4px;
4258 border-radius: 4px;
4266 border-radius: 4px;
4259 padding: 10px;
4267 padding: 10px;
4260 }
4268 }
4261
4269
4262 div.comment-form {
4270 div.comment-form {
4263 margin-top: 20px;
4271 margin-top: 20px;
4264 }
4272 }
4265
4273
4266 .comment-form strong {
4274 .comment-form strong {
4267 display: block;
4275 display: block;
4268 margin-bottom: 15px;
4276 margin-bottom: 15px;
4269 }
4277 }
4270
4278
4271 .comment-form textarea {
4279 .comment-form textarea {
4272 width: 100%;
4280 width: 100%;
4273 height: 100px;
4281 height: 100px;
4274 font-family: 'Monaco', 'Courier', 'Courier New', monospace;
4282 font-family: 'Monaco', 'Courier', 'Courier New', monospace;
4275 }
4283 }
4276
4284
4277 form.comment-form {
4285 form.comment-form {
4278 margin-top: 10px;
4286 margin-top: 10px;
4279 margin-left: 10px;
4287 margin-left: 10px;
4280 }
4288 }
4281
4289
4282 .comment-form-submit {
4290 .comment-form-submit {
4283 margin-top: 5px;
4291 margin-top: 5px;
4284 margin-left: 525px;
4292 margin-left: 525px;
4285 }
4293 }
4286
4294
4287 .file-comments {
4295 .file-comments {
4288 display: none;
4296 display: none;
4289 }
4297 }
4290
4298
4291 .comment-form .comment {
4299 .comment-form .comment {
4292 margin-left: 10px;
4300 margin-left: 10px;
4293 }
4301 }
4294
4302
4295 .comment-form .comment-help{
4303 .comment-form .comment-help{
4296 padding: 0px 0px 5px 0px;
4304 padding: 0px 0px 5px 0px;
4297 color: #666;
4305 color: #666;
4298 }
4306 }
4299
4307
4300 .comment-form .comment-button{
4308 .comment-form .comment-button{
4301 padding-top:5px;
4309 padding-top:5px;
4302 }
4310 }
4303
4311
4304 .add-another-button {
4312 .add-another-button {
4305 margin-left: 10px;
4313 margin-left: 10px;
4306 margin-top: 10px;
4314 margin-top: 10px;
4307 margin-bottom: 10px;
4315 margin-bottom: 10px;
4308 }
4316 }
4309
4317
4310 .comment .buttons {
4318 .comment .buttons {
4311 float: right;
4319 float: right;
4312 padding:2px 2px 0px 0px;
4320 padding:2px 2px 0px 0px;
4313 }
4321 }
4314
4322
4315
4323
4316 .show-inline-comments{
4324 .show-inline-comments{
4317 position: relative;
4325 position: relative;
4318 top:1px
4326 top:1px
4319 }
4327 }
4320
4328
4321 /** comment inline form **/
4329 /** comment inline form **/
4322 .comment-inline-form .overlay{
4330 .comment-inline-form .overlay{
4323 display: none;
4331 display: none;
4324 }
4332 }
4325 .comment-inline-form .overlay.submitting{
4333 .comment-inline-form .overlay.submitting{
4326 display:block;
4334 display:block;
4327 background: none repeat scroll 0 0 white;
4335 background: none repeat scroll 0 0 white;
4328 font-size: 16px;
4336 font-size: 16px;
4329 opacity: 0.5;
4337 opacity: 0.5;
4330 position: absolute;
4338 position: absolute;
4331 text-align: center;
4339 text-align: center;
4332 vertical-align: top;
4340 vertical-align: top;
4333
4341
4334 }
4342 }
4335 .comment-inline-form .overlay.submitting .overlay-text{
4343 .comment-inline-form .overlay.submitting .overlay-text{
4336 width:100%;
4344 width:100%;
4337 margin-top:5%;
4345 margin-top:5%;
4338 }
4346 }
4339
4347
4340 .comment-inline-form .clearfix{
4348 .comment-inline-form .clearfix{
4341 background: #EEE;
4349 background: #EEE;
4342 -webkit-border-radius: 4px;
4350 -webkit-border-radius: 4px;
4343 -moz-border-radius: 4px;
4351 -moz-border-radius: 4px;
4344 border-radius: 4px;
4352 border-radius: 4px;
4345 padding: 5px;
4353 padding: 5px;
4346 }
4354 }
4347
4355
4348 div.comment-inline-form {
4356 div.comment-inline-form {
4349 padding:4px 0px 6px 0px;
4357 padding:4px 0px 6px 0px;
4350 }
4358 }
4351
4359
4352
4360
4353 tr.hl-comment{
4361 tr.hl-comment{
4354 /*
4362 /*
4355 background-color: #FFFFCC !important;
4363 background-color: #FFFFCC !important;
4356 */
4364 */
4357 }
4365 }
4358
4366
4359 /*
4367 /*
4360 tr.hl-comment pre {
4368 tr.hl-comment pre {
4361 border-top: 2px solid #FFEE33;
4369 border-top: 2px solid #FFEE33;
4362 border-left: 2px solid #FFEE33;
4370 border-left: 2px solid #FFEE33;
4363 border-right: 2px solid #FFEE33;
4371 border-right: 2px solid #FFEE33;
4364 }
4372 }
4365 */
4373 */
4366
4374
4367 .comment-inline-form strong {
4375 .comment-inline-form strong {
4368 display: block;
4376 display: block;
4369 margin-bottom: 15px;
4377 margin-bottom: 15px;
4370 }
4378 }
4371
4379
4372 .comment-inline-form textarea {
4380 .comment-inline-form textarea {
4373 width: 100%;
4381 width: 100%;
4374 height: 100px;
4382 height: 100px;
4375 font-family: 'Monaco', 'Courier', 'Courier New', monospace;
4383 font-family: 'Monaco', 'Courier', 'Courier New', monospace;
4376 }
4384 }
4377
4385
4378 form.comment-inline-form {
4386 form.comment-inline-form {
4379 margin-top: 10px;
4387 margin-top: 10px;
4380 margin-left: 10px;
4388 margin-left: 10px;
4381 }
4389 }
4382
4390
4383 .comment-inline-form-submit {
4391 .comment-inline-form-submit {
4384 margin-top: 5px;
4392 margin-top: 5px;
4385 margin-left: 525px;
4393 margin-left: 525px;
4386 }
4394 }
4387
4395
4388 .file-comments {
4396 .file-comments {
4389 display: none;
4397 display: none;
4390 }
4398 }
4391
4399
4392 .comment-inline-form .comment {
4400 .comment-inline-form .comment {
4393 margin-left: 10px;
4401 margin-left: 10px;
4394 }
4402 }
4395
4403
4396 .comment-inline-form .comment-help{
4404 .comment-inline-form .comment-help{
4397 padding: 0px 0px 2px 0px;
4405 padding: 0px 0px 2px 0px;
4398 color: #666666;
4406 color: #666666;
4399 font-size: 10px;
4407 font-size: 10px;
4400 }
4408 }
4401
4409
4402 .comment-inline-form .comment-button{
4410 .comment-inline-form .comment-button{
4403 padding-top:5px;
4411 padding-top:5px;
4404 }
4412 }
4405
4413
4406 /** comment inline **/
4414 /** comment inline **/
4407 .inline-comments {
4415 .inline-comments {
4408 padding:10px 20px;
4416 padding:10px 20px;
4409 }
4417 }
4410
4418
4411 .inline-comments div.rst-block {
4419 .inline-comments div.rst-block {
4412 clear:both;
4420 clear:both;
4413 overflow:hidden;
4421 overflow:hidden;
4414 margin:0;
4422 margin:0;
4415 padding:0 20px 0px;
4423 padding:0 20px 0px;
4416 }
4424 }
4417 .inline-comments .comment {
4425 .inline-comments .comment {
4418 border: 1px solid #ddd;
4426 border: 1px solid #ddd;
4419 -webkit-border-radius: 4px;
4427 -webkit-border-radius: 4px;
4420 -moz-border-radius: 4px;
4428 -moz-border-radius: 4px;
4421 border-radius: 4px;
4429 border-radius: 4px;
4422 margin: 3px 3px 5px 5px;
4430 margin: 3px 3px 5px 5px;
4423 background-color: #FAFAFA;
4431 background-color: #FAFAFA;
4424 }
4432 }
4425 .inline-comments .add-comment {
4433 .inline-comments .add-comment {
4426 padding: 2px 4px 8px 5px;
4434 padding: 2px 4px 8px 5px;
4427 }
4435 }
4428
4436
4429 .inline-comments .comment-wrapp{
4437 .inline-comments .comment-wrapp{
4430 padding:1px;
4438 padding:1px;
4431 }
4439 }
4432 .inline-comments .comment .meta {
4440 .inline-comments .comment .meta {
4433 background: #f8f8f8;
4441 background: #f8f8f8;
4434 padding: 4px;
4442 padding: 4px;
4435 border-bottom: 1px solid #ddd;
4443 border-bottom: 1px solid #ddd;
4436 height: 20px;
4444 height: 20px;
4437 }
4445 }
4438
4446
4439 .inline-comments .comment .meta img {
4447 .inline-comments .comment .meta img {
4440 vertical-align: middle;
4448 vertical-align: middle;
4441 }
4449 }
4442
4450
4443 .inline-comments .comment .meta .user {
4451 .inline-comments .comment .meta .user {
4444 font-weight: bold;
4452 font-weight: bold;
4445 float:left;
4453 float:left;
4446 padding: 3px;
4454 padding: 3px;
4447 }
4455 }
4448
4456
4449 .inline-comments .comment .meta .date {
4457 .inline-comments .comment .meta .date {
4450 float:left;
4458 float:left;
4451 padding: 3px;
4459 padding: 3px;
4452 }
4460 }
4453
4461
4454 .inline-comments .comment .text {
4462 .inline-comments .comment .text {
4455 background-color: #FAFAFA;
4463 background-color: #FAFAFA;
4456 }
4464 }
4457
4465
4458 .inline-comments .comments-number{
4466 .inline-comments .comments-number{
4459 padding:0px 0px 10px 0px;
4467 padding:0px 0px 10px 0px;
4460 font-weight: bold;
4468 font-weight: bold;
4461 color: #666;
4469 color: #666;
4462 font-size: 16px;
4470 font-size: 16px;
4463 }
4471 }
4464 .inline-comments-button .add-comment{
4472 .inline-comments-button .add-comment{
4465 margin:2px 0px 8px 5px !important
4473 margin:2px 0px 8px 5px !important
4466 }
4474 }
4467
4475
4468
4476
4469 .notification-paginator{
4477 .notification-paginator{
4470 padding: 0px 0px 4px 16px;
4478 padding: 0px 0px 4px 16px;
4471 float: left;
4479 float: left;
4472 }
4480 }
4473
4481
4474 .notifications{
4482 .notifications{
4475 border-radius: 4px 4px 4px 4px;
4483 border-radius: 4px 4px 4px 4px;
4476 -webkit-border-radius: 4px;
4484 -webkit-border-radius: 4px;
4477 -moz-border-radius: 4px;
4485 -moz-border-radius: 4px;
4478 float: right;
4486 float: right;
4479 margin: 20px 0px 0px 0px;
4487 margin: 20px 0px 0px 0px;
4480 position: absolute;
4488 position: absolute;
4481 text-align: center;
4489 text-align: center;
4482 width: 26px;
4490 width: 26px;
4483 z-index: 1000;
4491 z-index: 1000;
4484 }
4492 }
4485 .notifications a{
4493 .notifications a{
4486 color:#888 !important;
4494 color:#888 !important;
4487 display: block;
4495 display: block;
4488 font-size: 10px;
4496 font-size: 10px;
4489 background-color: #DEDEDE !important;
4497 background-color: #DEDEDE !important;
4490 border-radius: 2px !important;
4498 border-radius: 2px !important;
4491 -webkit-border-radius: 2px !important;
4499 -webkit-border-radius: 2px !important;
4492 -moz-border-radius: 2px !important;
4500 -moz-border-radius: 2px !important;
4493 }
4501 }
4494 .notifications a:hover{
4502 .notifications a:hover{
4495 text-decoration: none !important;
4503 text-decoration: none !important;
4496 background-color: #EEEFFF !important;
4504 background-color: #EEEFFF !important;
4497 }
4505 }
4498 .notification-header{
4506 .notification-header{
4499 padding-top:6px;
4507 padding-top:6px;
4500 }
4508 }
4501 .notification-header .desc{
4509 .notification-header .desc{
4502 font-size: 16px;
4510 font-size: 16px;
4503 height: 24px;
4511 height: 24px;
4504 float: left
4512 float: left
4505 }
4513 }
4506 .notification-list .container.unread{
4514 .notification-list .container.unread{
4507 background: none repeat scroll 0 0 rgba(255, 255, 180, 0.6);
4515 background: none repeat scroll 0 0 rgba(255, 255, 180, 0.6);
4508 }
4516 }
4509 .notification-header .gravatar{
4517 .notification-header .gravatar{
4510 background: none repeat scroll 0 0 transparent;
4518 background: none repeat scroll 0 0 transparent;
4511 padding: 0px 0px 0px 8px;
4519 padding: 0px 0px 0px 8px;
4512 }
4520 }
4513 .notification-list .container .notification-header .desc{
4521 .notification-list .container .notification-header .desc{
4514 font-weight: bold;
4522 font-weight: bold;
4515 font-size: 17px;
4523 font-size: 17px;
4516 }
4524 }
4517 .notification-table{
4525 .notification-table{
4518 border: 1px solid #ccc;
4526 border: 1px solid #ccc;
4519 -webkit-border-radius: 6px 6px 6px 6px;
4527 -webkit-border-radius: 6px 6px 6px 6px;
4520 -moz-border-radius: 6px 6px 6px 6px;
4528 -moz-border-radius: 6px 6px 6px 6px;
4521 border-radius: 6px 6px 6px 6px;
4529 border-radius: 6px 6px 6px 6px;
4522 clear: both;
4530 clear: both;
4523 margin: 0px 20px 0px 20px;
4531 margin: 0px 20px 0px 20px;
4524 }
4532 }
4525 .notification-header .delete-notifications{
4533 .notification-header .delete-notifications{
4526 float: right;
4534 float: right;
4527 padding-top: 8px;
4535 padding-top: 8px;
4528 cursor: pointer;
4536 cursor: pointer;
4529 }
4537 }
4530 .notification-header .read-notifications{
4538 .notification-header .read-notifications{
4531 float: right;
4539 float: right;
4532 padding-top: 8px;
4540 padding-top: 8px;
4533 cursor: pointer;
4541 cursor: pointer;
4534 }
4542 }
4535 .notification-subject{
4543 .notification-subject{
4536 clear:both;
4544 clear:both;
4537 border-bottom: 1px solid #eee;
4545 border-bottom: 1px solid #eee;
4538 padding:5px 0px 5px 38px;
4546 padding:5px 0px 5px 38px;
4539 }
4547 }
4540
4548
4541 .notification-body{
4549 .notification-body{
4542 clear:both;
4550 clear:both;
4543 margin: 34px 2px 2px 8px
4551 margin: 34px 2px 2px 8px
4544 }
4552 }
4545
4553
4546 /****
4554 /****
4547 PULL REQUESTS
4555 PULL REQUESTS
4548 *****/
4556 *****/
4549 .pullrequests_section_head {
4557 .pullrequests_section_head {
4550 padding:10px 10px 10px 0px;
4558 padding:10px 10px 10px 0px;
4551 font-size:16px;
4559 font-size:16px;
4552 font-weight: bold;
4560 font-weight: bold;
4553 }
4561 }
4554
4562
4555 /****
4563 /****
4556 PERMS
4564 PERMS
4557 *****/
4565 *****/
4558 #perms .perms_section_head {
4566 #perms .perms_section_head {
4559 padding:10px 10px 10px 0px;
4567 padding:10px 10px 10px 0px;
4560 font-size:16px;
4568 font-size:16px;
4561 font-weight: bold;
4569 font-weight: bold;
4562 }
4570 }
4563
4571
4564 #perms .perm_tag{
4572 #perms .perm_tag{
4565 padding: 1px 3px 1px 3px;
4573 padding: 1px 3px 1px 3px;
4566 font-size: 10px;
4574 font-size: 10px;
4567 font-weight: bold;
4575 font-weight: bold;
4568 text-transform: uppercase;
4576 text-transform: uppercase;
4569 white-space: nowrap;
4577 white-space: nowrap;
4570 -webkit-border-radius: 3px;
4578 -webkit-border-radius: 3px;
4571 -moz-border-radius: 3px;
4579 -moz-border-radius: 3px;
4572 border-radius: 3px;
4580 border-radius: 3px;
4573 }
4581 }
4574
4582
4575 #perms .perm_tag.admin{
4583 #perms .perm_tag.admin{
4576 background-color: #B94A48;
4584 background-color: #B94A48;
4577 color: #ffffff;
4585 color: #ffffff;
4578 }
4586 }
4579
4587
4580 #perms .perm_tag.write{
4588 #perms .perm_tag.write{
4581 background-color: #B94A48;
4589 background-color: #B94A48;
4582 color: #ffffff;
4590 color: #ffffff;
4583 }
4591 }
4584
4592
4585 #perms .perm_tag.read{
4593 #perms .perm_tag.read{
4586 background-color: #468847;
4594 background-color: #468847;
4587 color: #ffffff;
4595 color: #ffffff;
4588 }
4596 }
4589
4597
4590 #perms .perm_tag.none{
4598 #perms .perm_tag.none{
4591 background-color: #bfbfbf;
4599 background-color: #bfbfbf;
4592 color: #ffffff;
4600 color: #ffffff;
4593 }
4601 }
4594
4602
4595 .perm-gravatar{
4603 .perm-gravatar{
4596 vertical-align:middle;
4604 vertical-align:middle;
4597 padding:2px;
4605 padding:2px;
4598 }
4606 }
4599 .perm-gravatar-ac{
4607 .perm-gravatar-ac{
4600 vertical-align:middle;
4608 vertical-align:middle;
4601 padding:2px;
4609 padding:2px;
4602 width: 14px;
4610 width: 14px;
4603 height: 14px;
4611 height: 14px;
4604 }
4612 }
4605
4613
4606 /*****************************************************************************
4614 /*****************************************************************************
4607 DIFFS CSS
4615 DIFFS CSS
4608 ******************************************************************************/
4616 ******************************************************************************/
4609
4617
4610 div.diffblock {
4618 div.diffblock {
4611 overflow: auto;
4619 overflow: auto;
4612 padding: 0px;
4620 padding: 0px;
4613 border: 1px solid #ccc;
4621 border: 1px solid #ccc;
4614 background: #f8f8f8;
4622 background: #f8f8f8;
4615 font-size: 100%;
4623 font-size: 100%;
4616 line-height: 100%;
4624 line-height: 100%;
4617 /* new */
4625 /* new */
4618 line-height: 125%;
4626 line-height: 125%;
4619 -webkit-border-radius: 6px 6px 0px 0px;
4627 -webkit-border-radius: 6px 6px 0px 0px;
4620 -moz-border-radius: 6px 6px 0px 0px;
4628 -moz-border-radius: 6px 6px 0px 0px;
4621 border-radius: 6px 6px 0px 0px;
4629 border-radius: 6px 6px 0px 0px;
4622 }
4630 }
4623 div.diffblock.margined{
4631 div.diffblock.margined{
4624 margin: 0px 20px 0px 20px;
4632 margin: 0px 20px 0px 20px;
4625 }
4633 }
4626 div.diffblock .code-header{
4634 div.diffblock .code-header{
4627 border-bottom: 1px solid #CCCCCC;
4635 border-bottom: 1px solid #CCCCCC;
4628 background: #EEEEEE;
4636 background: #EEEEEE;
4629 padding:10px 0 10px 0;
4637 padding:10px 0 10px 0;
4630 height: 14px;
4638 height: 14px;
4631 }
4639 }
4632 div.diffblock .code-header.cv{
4640 div.diffblock .code-header.cv{
4633 height: 34px;
4641 height: 34px;
4634 }
4642 }
4635 div.diffblock .code-header-title{
4643 div.diffblock .code-header-title{
4636 padding: 0px 0px 10px 5px !important;
4644 padding: 0px 0px 10px 5px !important;
4637 margin: 0 !important;
4645 margin: 0 !important;
4638 }
4646 }
4639 div.diffblock .code-header .hash{
4647 div.diffblock .code-header .hash{
4640 float: left;
4648 float: left;
4641 padding: 2px 0 0 2px;
4649 padding: 2px 0 0 2px;
4642 }
4650 }
4643 div.diffblock .code-header .date{
4651 div.diffblock .code-header .date{
4644 float:left;
4652 float:left;
4645 text-transform: uppercase;
4653 text-transform: uppercase;
4646 padding: 2px 0px 0px 2px;
4654 padding: 2px 0px 0px 2px;
4647 }
4655 }
4648 div.diffblock .code-header div{
4656 div.diffblock .code-header div{
4649 margin-left:4px;
4657 margin-left:4px;
4650 font-weight: bold;
4658 font-weight: bold;
4651 font-size: 14px;
4659 font-size: 14px;
4652 }
4660 }
4653 div.diffblock .code-body{
4661 div.diffblock .code-body{
4654 background: #FFFFFF;
4662 background: #FFFFFF;
4655 }
4663 }
4656 div.diffblock pre.raw{
4664 div.diffblock pre.raw{
4657 background: #FFFFFF;
4665 background: #FFFFFF;
4658 color:#000000;
4666 color:#000000;
4659 }
4667 }
4660 table.code-difftable{
4668 table.code-difftable{
4661 border-collapse: collapse;
4669 border-collapse: collapse;
4662 width: 99%;
4670 width: 99%;
4663 }
4671 }
4664 table.code-difftable td {
4672 table.code-difftable td {
4665 padding: 0 !important;
4673 padding: 0 !important;
4666 background: none !important;
4674 background: none !important;
4667 border:0 !important;
4675 border:0 !important;
4668 vertical-align: none !important;
4676 vertical-align: none !important;
4669 }
4677 }
4670 table.code-difftable .context{
4678 table.code-difftable .context{
4671 background:none repeat scroll 0 0 #DDE7EF;
4679 background:none repeat scroll 0 0 #DDE7EF;
4672 }
4680 }
4673 table.code-difftable .add{
4681 table.code-difftable .add{
4674 background:none repeat scroll 0 0 #DDFFDD;
4682 background:none repeat scroll 0 0 #DDFFDD;
4675 }
4683 }
4676 table.code-difftable .add ins{
4684 table.code-difftable .add ins{
4677 background:none repeat scroll 0 0 #AAFFAA;
4685 background:none repeat scroll 0 0 #AAFFAA;
4678 text-decoration:none;
4686 text-decoration:none;
4679 }
4687 }
4680 table.code-difftable .del{
4688 table.code-difftable .del{
4681 background:none repeat scroll 0 0 #FFDDDD;
4689 background:none repeat scroll 0 0 #FFDDDD;
4682 }
4690 }
4683 table.code-difftable .del del{
4691 table.code-difftable .del del{
4684 background:none repeat scroll 0 0 #FFAAAA;
4692 background:none repeat scroll 0 0 #FFAAAA;
4685 text-decoration:none;
4693 text-decoration:none;
4686 }
4694 }
4687
4695
4688 /** LINE NUMBERS **/
4696 /** LINE NUMBERS **/
4689 table.code-difftable .lineno{
4697 table.code-difftable .lineno{
4690
4698
4691 padding-left:2px;
4699 padding-left:2px;
4692 padding-right:2px;
4700 padding-right:2px;
4693 text-align:right;
4701 text-align:right;
4694 width:32px;
4702 width:32px;
4695 -moz-user-select:none;
4703 -moz-user-select:none;
4696 -webkit-user-select: none;
4704 -webkit-user-select: none;
4697 border-right: 1px solid #CCC !important;
4705 border-right: 1px solid #CCC !important;
4698 border-left: 0px solid #CCC !important;
4706 border-left: 0px solid #CCC !important;
4699 border-top: 0px solid #CCC !important;
4707 border-top: 0px solid #CCC !important;
4700 border-bottom: none !important;
4708 border-bottom: none !important;
4701 vertical-align: middle !important;
4709 vertical-align: middle !important;
4702
4710
4703 }
4711 }
4704 table.code-difftable .lineno.new {
4712 table.code-difftable .lineno.new {
4705 }
4713 }
4706 table.code-difftable .lineno.old {
4714 table.code-difftable .lineno.old {
4707 }
4715 }
4708 table.code-difftable .lineno a{
4716 table.code-difftable .lineno a{
4709 color:#747474 !important;
4717 color:#747474 !important;
4710 font:11px "Bitstream Vera Sans Mono",Monaco,"Courier New",Courier,monospace !important;
4718 font:11px "Bitstream Vera Sans Mono",Monaco,"Courier New",Courier,monospace !important;
4711 letter-spacing:-1px;
4719 letter-spacing:-1px;
4712 text-align:right;
4720 text-align:right;
4713 padding-right: 2px;
4721 padding-right: 2px;
4714 cursor: pointer;
4722 cursor: pointer;
4715 display: block;
4723 display: block;
4716 width: 32px;
4724 width: 32px;
4717 }
4725 }
4718
4726
4719 table.code-difftable .lineno-inline{
4727 table.code-difftable .lineno-inline{
4720 background:none repeat scroll 0 0 #FFF !important;
4728 background:none repeat scroll 0 0 #FFF !important;
4721 padding-left:2px;
4729 padding-left:2px;
4722 padding-right:2px;
4730 padding-right:2px;
4723 text-align:right;
4731 text-align:right;
4724 width:30px;
4732 width:30px;
4725 -moz-user-select:none;
4733 -moz-user-select:none;
4726 -webkit-user-select: none;
4734 -webkit-user-select: none;
4727 }
4735 }
4728
4736
4729 /** CODE **/
4737 /** CODE **/
4730 table.code-difftable .code {
4738 table.code-difftable .code {
4731 display: block;
4739 display: block;
4732 width: 100%;
4740 width: 100%;
4733 }
4741 }
4734 table.code-difftable .code td{
4742 table.code-difftable .code td{
4735 margin:0;
4743 margin:0;
4736 padding:0;
4744 padding:0;
4737 }
4745 }
4738 table.code-difftable .code pre{
4746 table.code-difftable .code pre{
4739 margin:0;
4747 margin:0;
4740 padding:0;
4748 padding:0;
4741 height: 17px;
4749 height: 17px;
4742 line-height: 17px;
4750 line-height: 17px;
4743 }
4751 }
4744
4752
4745
4753
4746 .diffblock.margined.comm .line .code:hover{
4754 .diffblock.margined.comm .line .code:hover{
4747 background-color:#FFFFCC !important;
4755 background-color:#FFFFCC !important;
4748 cursor: pointer !important;
4756 cursor: pointer !important;
4749 background-image:url("../images/icons/comment_add.png") !important;
4757 background-image:url("../images/icons/comment_add.png") !important;
4750 background-repeat:no-repeat !important;
4758 background-repeat:no-repeat !important;
4751 background-position: right !important;
4759 background-position: right !important;
4752 background-position: 0% 50% !important;
4760 background-position: 0% 50% !important;
4753 }
4761 }
4754 .diffblock.margined.comm .line .code.no-comment:hover{
4762 .diffblock.margined.comm .line .code.no-comment:hover{
4755 background-image: none !important;
4763 background-image: none !important;
4756 cursor: auto !important;
4764 cursor: auto !important;
4757 background-color: inherit !important;
4765 background-color: inherit !important;
4758
4766
4759 }
4767 }
@@ -1,77 +1,77 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2
2
3 ${h.form(url('repos'))}
3 ${h.form(url('repos'))}
4 <div class="form">
4 <div class="form">
5 <!-- fields -->
5 <!-- fields -->
6 <div class="fields">
6 <div class="fields">
7 <div class="field">
7 <div class="field">
8 <div class="label">
8 <div class="label">
9 <label for="repo_name">${_('Name')}:</label>
9 <label for="repo_name">${_('Name')}:</label>
10 </div>
10 </div>
11 <div class="input">
11 <div class="input">
12 ${h.text('repo_name',c.new_repo,class_="small")}
12 ${h.text('repo_name',c.new_repo,class_="small")}
13 %if not h.HasPermissionAll('hg.admin')('repo create form'):
13 %if not h.HasPermissionAll('hg.admin')('repo create form'):
14 ${h.hidden('user_created',True)}
14 ${h.hidden('user_created',True)}
15 %endif
15 %endif
16 </div>
16 </div>
17 </div>
17 </div>
18 <div class="field">
18 <div class="field">
19 <div class="label">
19 <div class="label">
20 <label for="clone_uri">${_('Clone from')}:</label>
20 <label for="clone_uri">${_('Clone from')}:</label>
21 </div>
21 </div>
22 <div class="input">
22 <div class="input">
23 ${h.text('clone_uri',class_="small")}
23 ${h.text('clone_uri',class_="small")}
24 <span class="help-block">${_('Optional http[s] url from which repository should be cloned.')}</span>
24 <span class="help-block">${_('Optional http[s] url from which repository should be cloned.')}</span>
25 </div>
25 </div>
26 </div>
26 </div>
27 <div class="field">
27 <div class="field">
28 <div class="label">
28 <div class="label">
29 <label for="repo_group">${_('Repository group')}:</label>
29 <label for="repo_group">${_('Repository group')}:</label>
30 </div>
30 </div>
31 <div class="input">
31 <div class="input">
32 ${h.select('repo_group',request.GET.get('parent_group'),c.repo_groups,class_="medium")}
32 ${h.select('repo_group',request.GET.get('parent_group'),c.repo_groups,class_="medium")}
33 <span class="help-block">${_('Optionaly select a group to put this repository into.')}</span>
33 <span class="help-block">${_('Optionaly select a group to put this repository into.')}</span>
34 </div>
34 </div>
35 </div>
35 </div>
36 <div class="field">
36 <div class="field">
37 <div class="label">
37 <div class="label">
38 <label for="repo_type">${_('Type')}:</label>
38 <label for="repo_type">${_('Type')}:</label>
39 </div>
39 </div>
40 <div class="input">
40 <div class="input">
41 ${h.select('repo_type','hg',c.backends,class_="small")}
41 ${h.select('repo_type','hg',c.backends,class_="small")}
42 <span class="help-block">${_('Type of repository to create.')}</span>
42 <span class="help-block">${_('Type of repository to create.')}</span>
43 </div>
43 </div>
44 </div>
44 </div>
45 <div class="field">
45 <div class="field">
46 <div class="label">
46 <div class="label">
47 <label for="landing_rev">${_('Landing revision')}:</label>
47 <label for="repo_landing_rev">${_('Landing revision')}:</label>
48 </div>
48 </div>
49 <div class="input">
49 <div class="input">
50 ${h.select('landing_rev','',c.landing_revs,class_="medium")}
50 ${h.select('repo_landing_rev','',c.landing_revs,class_="medium")}
51 <span class="help-block">${_('Default revision for files page, downloads, whoosh and readme')}</span>
51 <span class="help-block">${_('Default revision for files page, downloads, whoosh and readme')}</span>
52 </div>
52 </div>
53 </div>
53 </div>
54 <div class="field">
54 <div class="field">
55 <div class="label label-textarea">
55 <div class="label label-textarea">
56 <label for="description">${_('Description')}:</label>
56 <label for="repo_description">${_('Description')}:</label>
57 </div>
57 </div>
58 <div class="textarea text-area editor">
58 <div class="textarea text-area editor">
59 ${h.textarea('description')}
59 ${h.textarea('repo_description')}
60 <span class="help-block">${_('Keep it short and to the point. Use a README file for longer descriptions.')}</span>
60 <span class="help-block">${_('Keep it short and to the point. Use a README file for longer descriptions.')}</span>
61 </div>
61 </div>
62 </div>
62 </div>
63 <div class="field">
63 <div class="field">
64 <div class="label label-checkbox">
64 <div class="label label-checkbox">
65 <label for="private">${_('Private repository')}:</label>
65 <label for="repo_private">${_('Private repository')}:</label>
66 </div>
66 </div>
67 <div class="checkboxes">
67 <div class="checkboxes">
68 ${h.checkbox('private',value="True")}
68 ${h.checkbox('repo_private',value="True")}
69 <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span>
69 <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span>
70 </div>
70 </div>
71 </div>
71 </div>
72 <div class="buttons">
72 <div class="buttons">
73 ${h.submit('add',_('add'),class_="ui-btn large")}
73 ${h.submit('add',_('add'),class_="ui-btn large")}
74 </div>
74 </div>
75 </div>
75 </div>
76 </div>
76 </div>
77 ${h.end_form()}
77 ${h.end_form()}
@@ -1,361 +1,362 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="root.html"/>
2 <%inherit file="root.html"/>
3
3
4 <!-- HEADER -->
4 <!-- HEADER -->
5 <div id="header">
5 <div id="header">
6 <div id="header-inner" class="title hover">
6 <div id="header-inner" class="title hover">
7 <div id="logo">
7 <div id="logo">
8 <h1><a href="${h.url('home')}">${c.rhodecode_name}</a></h1>
8 <h1><a href="${h.url('home')}">${c.rhodecode_name}</a></h1>
9 </div>
9 </div>
10 <!-- MENU -->
10 <!-- MENU -->
11 ${self.page_nav()}
11 ${self.page_nav()}
12 <!-- END MENU -->
12 <!-- END MENU -->
13 ${self.body()}
13 ${self.body()}
14 </div>
14 </div>
15 </div>
15 </div>
16 <!-- END HEADER -->
16 <!-- END HEADER -->
17
17
18 <!-- CONTENT -->
18 <!-- CONTENT -->
19 <div id="content">
19 <div id="content">
20 <div class="flash_msg">
20 <div class="flash_msg">
21 <% messages = h.flash.pop_messages() %>
21 <% messages = h.flash.pop_messages() %>
22 % if messages:
22 % if messages:
23 <ul id="flash-messages">
23 <ul id="flash-messages">
24 % for message in messages:
24 % for message in messages:
25 <li class="${message.category}_msg">${message}</li>
25 <li class="${message.category}_msg">${message}</li>
26 % endfor
26 % endfor
27 </ul>
27 </ul>
28 % endif
28 % endif
29 </div>
29 </div>
30 <div id="main">
30 <div id="main">
31 ${next.main()}
31 ${next.main()}
32 </div>
32 </div>
33 </div>
33 </div>
34 <!-- END CONTENT -->
34 <!-- END CONTENT -->
35
35
36 <!-- FOOTER -->
36 <!-- FOOTER -->
37 <div id="footer">
37 <div id="footer">
38 <div id="footer-inner" class="title">
38 <div id="footer-inner" class="title">
39 <div>
39 <div>
40 <p class="footer-link">
40 <p class="footer-link">
41 <a href="${h.url('bugtracker')}">${_('Submit a bug')}</a>
41 <a href="${h.url('bugtracker')}">${_('Submit a bug')}</a>
42 </p>
42 </p>
43 <p class="footer-link-right">
43 <p class="footer-link-right">
44 <a href="${h.url('rhodecode_official')}">RhodeCode${'-%s' % c.rhodecode_instanceid if c.rhodecode_instanceid else ''}</a>
44 <a href="${h.url('rhodecode_official')}">RhodeCode${'-%s' % c.rhodecode_instanceid if c.rhodecode_instanceid else ''}</a>
45 ${c.rhodecode_version} &copy; 2010-${h.datetime.today().year} by Marcin Kuzminski
45 ${c.rhodecode_version} &copy; 2010-${h.datetime.today().year} by Marcin Kuzminski
46 </p>
46 </p>
47 </div>
47 </div>
48 </div>
48 </div>
49 </div>
49 </div>
50 <!-- END FOOTER -->
50 <!-- END FOOTER -->
51
51
52 ### MAKO DEFS ###
52 ### MAKO DEFS ###
53 <%def name="page_nav()">
53 <%def name="page_nav()">
54 ${self.menu()}
54 ${self.menu()}
55 </%def>
55 </%def>
56
56
57 <%def name="breadcrumbs()">
57 <%def name="breadcrumbs()">
58 <div class="breadcrumbs">
58 <div class="breadcrumbs">
59 ${self.breadcrumbs_links()}
59 ${self.breadcrumbs_links()}
60 </div>
60 </div>
61 </%def>
61 </%def>
62
62
63 <%def name="usermenu()">
63 <%def name="usermenu()">
64 <div class="user-menu">
64 <div class="user-menu">
65 <div class="container">
65 <div class="container">
66 <div class="gravatar" id="quick_login_link">
66 <div class="gravatar" id="quick_login_link">
67 <img alt="gravatar" src="${h.gravatar_url(c.rhodecode_user.email,24)}" />
67 <img alt="gravatar" src="${h.gravatar_url(c.rhodecode_user.email,24)}" />
68 </div>
68 </div>
69 %if c.rhodecode_user.username != 'default' and c.unread_notifications != 0:
69 %if c.rhodecode_user.username != 'default' and c.unread_notifications != 0:
70 <div class="notifications">
70 <div class="notifications">
71 <a id="notification_counter" href="${h.url('notifications')}">${c.unread_notifications}</a>
71 <a id="notification_counter" href="${h.url('notifications')}">${c.unread_notifications}</a>
72 </div>
72 </div>
73 %endif
73 %endif
74 </div>
74 </div>
75 <div id="quick_login" style="display:none">
75 <div id="quick_login" style="display:none">
76 %if c.rhodecode_user.username == 'default':
76 %if c.rhodecode_user.username == 'default':
77 <h4>${_('Login to your account')}</h4>
77 <h4>${_('Login to your account')}</h4>
78 ${h.form(h.url('login_home',came_from=h.url.current()))}
78 ${h.form(h.url('login_home',came_from=h.url.current()))}
79 <div class="form">
79 <div class="form">
80 <div class="fields">
80 <div class="fields">
81 <div class="field">
81 <div class="field">
82 <div class="label">
82 <div class="label">
83 <label for="username">${_('Username')}:</label>
83 <label for="username">${_('Username')}:</label>
84 </div>
84 </div>
85 <div class="input">
85 <div class="input">
86 ${h.text('username',class_='focus',size=40)}
86 ${h.text('username',class_='focus',size=40)}
87 </div>
87 </div>
88
88
89 </div>
89 </div>
90 <div class="field">
90 <div class="field">
91 <div class="label">
91 <div class="label">
92 <label for="password">${_('Password')}:</label>
92 <label for="password">${_('Password')}:</label>
93 </div>
93 </div>
94 <div class="input">
94 <div class="input">
95 ${h.password('password',class_='focus',size=40)}
95 ${h.password('password',class_='focus',size=40)}
96 </div>
96 </div>
97
97
98 </div>
98 </div>
99 <div class="buttons">
99 <div class="buttons">
100 <div class="password_forgoten">${h.link_to(_('Forgot password ?'),h.url('reset_password'))}</div>
100 <div class="password_forgoten">${h.link_to(_('Forgot password ?'),h.url('reset_password'))}</div>
101 <div class="register">
101 <div class="register">
102 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
102 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
103 ${h.link_to(_("Don't have an account ?"),h.url('register'))}
103 ${h.link_to(_("Don't have an account ?"),h.url('register'))}
104 %endif
104 %endif
105 </div>
105 </div>
106 <div class="submit">
106 <div class="submit">
107 ${h.submit('sign_in',_('Log In'),class_="ui-btn xsmall")}
107 ${h.submit('sign_in',_('Log In'),class_="ui-btn xsmall")}
108 </div>
108 </div>
109 </div>
109 </div>
110 </div>
110 </div>
111 </div>
111 </div>
112 ${h.end_form()}
112 ${h.end_form()}
113 %else:
113 %else:
114 <div class="links_left">
114 <div class="links_left">
115 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
115 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
116 <div class="email">${c.rhodecode_user.email}</div>
116 <div class="email">${c.rhodecode_user.email}</div>
117 <div class="big_gravatar"><img alt="gravatar" src="${h.gravatar_url(c.rhodecode_user.email,48)}" /></div>
117 <div class="big_gravatar"><img alt="gravatar" src="${h.gravatar_url(c.rhodecode_user.email,48)}" /></div>
118 <div class="inbox"><a href="${h.url('notifications')}">${_('Inbox')}: ${c.unread_notifications}</a></div>
118 <div class="inbox"><a href="${h.url('notifications')}">${_('Inbox')}: ${c.unread_notifications}</a></div>
119 </div>
119 </div>
120 <div class="links_right">
120 <div class="links_right">
121 <ol class="links">
121 <ol class="links">
122 <li>${h.link_to(_(u'Home'),h.url('home'))}</li>
122 <li>${h.link_to(_(u'Home'),h.url('home'))}</li>
123 <li>${h.link_to(_(u'Journal'),h.url('journal'))}</li>
123 <li>${h.link_to(_(u'Journal'),h.url('journal'))}</li>
124 <li>${h.link_to(_(u'My account'),h.url('admin_settings_my_account'))}</li>
124 <li>${h.link_to(_(u'My account'),h.url('admin_settings_my_account'))}</li>
125 <li class="logout">${h.link_to(_(u'Log Out'),h.url('logout_home'))}</li>
125 <li class="logout">${h.link_to(_(u'Log Out'),h.url('logout_home'))}</li>
126 </ol>
126 </ol>
127 </div>
127 </div>
128 %endif
128 %endif
129 </div>
129 </div>
130 </div>
130 </div>
131 </%def>
131 </%def>
132
132
133 <%def name="menu(current=None)">
133 <%def name="menu(current=None)">
134 <%
134 <%
135 def is_current(selected):
135 def is_current(selected):
136 if selected == current:
136 if selected == current:
137 return h.literal('class="current"')
137 return h.literal('class="current"')
138 %>
138 %>
139 %if current not in ['home','admin']:
139 %if current not in ['home','admin']:
140 ##REGULAR MENU
140 ##REGULAR MENU
141 <ul id="quick">
141 <ul id="quick">
142 <!-- repo switcher -->
142 <!-- repo switcher -->
143 <li>
143 <li>
144 <a class="menu_link" id="repo_switcher" title="${_('Switch repository')}" href="#">
144 <a class="menu_link" id="repo_switcher" title="${_('Switch repository')}" href="#">
145 <span class="icon">
145 <span class="icon">
146 <img src="${h.url('/images/icons/database.png')}" alt="${_('Products')}" />
146 <img src="${h.url('/images/icons/database.png')}" alt="${_('Products')}" />
147 </span>
147 </span>
148 <span>&darr;</span>
148 <span>&darr;</span>
149 </a>
149 </a>
150 <ul id="repo_switcher_list" class="repo_switcher">
150 <ul id="repo_switcher_list" class="repo_switcher">
151 <li>
151 <li>
152 <a href="#">${_('loading...')}</a>
152 <a href="#">${_('loading...')}</a>
153 </li>
153 </li>
154 </ul>
154 </ul>
155 </li>
155 </li>
156
156
157 <li ${is_current('summary')}>
157 <li ${is_current('summary')}>
158 <a class="menu_link" title="${_('Summary')}" href="${h.url('summary_home',repo_name=c.repo_name)}">
158 <a class="menu_link" title="${_('Summary')}" href="${h.url('summary_home',repo_name=c.repo_name)}">
159 <span class="icon">
159 <span class="icon">
160 <img src="${h.url('/images/icons/clipboard_16.png')}" alt="${_('Summary')}" />
160 <img src="${h.url('/images/icons/clipboard_16.png')}" alt="${_('Summary')}" />
161 </span>
161 </span>
162 <span>${_('Summary')}</span>
162 <span>${_('Summary')}</span>
163 </a>
163 </a>
164 </li>
164 </li>
165 <li ${is_current('changelog')}>
165 <li ${is_current('changelog')}>
166 <a class="menu_link" title="${_('Changelog')}" href="${h.url('changelog_home',repo_name=c.repo_name)}">
166 <a class="menu_link" title="${_('Changelog')}" href="${h.url('changelog_home',repo_name=c.repo_name)}">
167 <span class="icon">
167 <span class="icon">
168 <img src="${h.url('/images/icons/time.png')}" alt="${_('Changelog')}" />
168 <img src="${h.url('/images/icons/time.png')}" alt="${_('Changelog')}" />
169 </span>
169 </span>
170 <span>${_('Changelog')}</span>
170 <span>${_('Changelog')}</span>
171 </a>
171 </a>
172 </li>
172 </li>
173
173
174 <li ${is_current('switch_to')}>
174 <li ${is_current('switch_to')}>
175 <a class="menu_link" id="branch_tag_switcher" title="${_('Switch to')}" href="#">
175 <a class="menu_link" id="branch_tag_switcher" title="${_('Switch to')}" href="#">
176 <span class="icon">
176 <span class="icon">
177 <img src="${h.url('/images/icons/arrow_switch.png')}" alt="${_('Switch to')}" />
177 <img src="${h.url('/images/icons/arrow_switch.png')}" alt="${_('Switch to')}" />
178 </span>
178 </span>
179 <span>${_('Switch to')}</span>
179 <span>${_('Switch to')}</span>
180 </a>
180 </a>
181 <ul id="switch_to_list" class="switch_to">
181 <ul id="switch_to_list" class="switch_to">
182 <li><a href="#">${_('loading...')}</a></li>
182 <li><a href="#">${_('loading...')}</a></li>
183 </ul>
183 </ul>
184 </li>
184 </li>
185 <li ${is_current('files')}>
185 <li ${is_current('files')}>
186 <a class="menu_link" title="${_('Files')}" href="${h.url('files_home',repo_name=c.repo_name)}">
186 <a class="menu_link" title="${_('Files')}" href="${h.url('files_home',repo_name=c.repo_name)}">
187 <span class="icon">
187 <span class="icon">
188 <img src="${h.url('/images/icons/file.png')}" alt="${_('Files')}" />
188 <img src="${h.url('/images/icons/file.png')}" alt="${_('Files')}" />
189 </span>
189 </span>
190 <span>${_('Files')}</span>
190 <span>${_('Files')}</span>
191 </a>
191 </a>
192 </li>
192 </li>
193
193
194 <li ${is_current('options')}>
194 <li ${is_current('options')}>
195 <a class="menu_link" title="${_('Options')}" href="#">
195 <a class="menu_link" title="${_('Options')}" href="#">
196 <span class="icon">
196 <span class="icon">
197 <img src="${h.url('/images/icons/table_gear.png')}" alt="${_('Admin')}" />
197 <img src="${h.url('/images/icons/table_gear.png')}" alt="${_('Admin')}" />
198 </span>
198 </span>
199 <span>${_('Options')}</span>
199 <span>${_('Options')}</span>
200 </a>
200 </a>
201 <ul>
201 <ul>
202 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
202 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
203 %if h.HasPermissionAll('hg.admin')('access settings on repository'):
203 %if h.HasPermissionAll('hg.admin')('access settings on repository'):
204 <li>${h.link_to(_('repository settings'),h.url('edit_repo',repo_name=c.repo_name),class_='settings')}</li>
204 <li>${h.link_to(_('repository settings'),h.url('edit_repo',repo_name=c.repo_name),class_='settings')}</li>
205 %else:
205 %else:
206 <li>${h.link_to(_('repository settings'),h.url('repo_settings_home',repo_name=c.repo_name),class_='settings')}</li>
206 <li>${h.link_to(_('repository settings'),h.url('repo_settings_home',repo_name=c.repo_name),class_='settings')}</li>
207 %endif
207 %endif
208 %endif
208 %endif
209
209
210 <li>${h.link_to(_('fork'),h.url('repo_fork_home',repo_name=c.repo_name),class_='fork')}</li>
210 <li>${h.link_to(_('fork'),h.url('repo_fork_home',repo_name=c.repo_name),class_='fork')}</li>
211 %if h.is_hg(c.rhodecode_repo):
211 %if h.is_hg(c.rhodecode_repo):
212 <li>${h.link_to(_('Open new pull request'),h.url('pullrequest_home',repo_name=c.repo_name),class_='pull_request')}</li>
212 <li>${h.link_to(_('Open new pull request'),h.url('pullrequest_home',repo_name=c.repo_name),class_='pull_request')}</li>
213 %endif
213 %endif
214 %if c.rhodecode_db_repo.fork:
214 %if c.rhodecode_db_repo.fork:
215 <li>${h.link_to(_('Compare fork'),h.url('compare_url',repo_name=c.repo_name,org_ref_type='branch',org_ref=request.GET.get('branch') or 'default',other_ref_type='branch',other_ref='default',repo=c.rhodecode_db_repo.fork.repo_name),class_='compare_request')}</li>
215 <li>${h.link_to(_('Compare fork'),h.url('compare_url',repo_name=c.repo_name,org_ref_type='branch',org_ref=request.GET.get('branch') or 'default',other_ref_type='branch',other_ref='default',repo=c.rhodecode_db_repo.fork.repo_name),class_='compare_request')}</li>
216 %endif
216 %endif
217 <li>${h.link_to(_('search'),h.url('search_repo',search_repo=c.repo_name),class_='search')}</li>
217 <li>${h.link_to(_('search'),h.url('search_repo',search_repo=c.repo_name),class_='search')}</li>
218
218
219 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking:
219 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking:
220 %if c.rhodecode_db_repo.locked[0]:
220 %if c.rhodecode_db_repo.locked[0]:
221 <li>${h.link_to(_('unlock'), h.url('toggle_locking',repo_name=c.repo_name),class_='locking_del')}</li>
221 <li>${h.link_to(_('unlock'), h.url('toggle_locking',repo_name=c.repo_name),class_='locking_del')}</li>
222 %else:
222 %else:
223 <li>${h.link_to(_('lock'), h.url('toggle_locking',repo_name=c.repo_name),class_='locking_add')}</li>
223 <li>${h.link_to(_('lock'), h.url('toggle_locking',repo_name=c.repo_name),class_='locking_add')}</li>
224 %endif
224 %endif
225 %endif
225 %endif
226
226
227 % if h.HasPermissionAll('hg.admin')('access admin main page'):
227 % if h.HasPermissionAll('hg.admin')('access admin main page'):
228 <li>
228 <li>
229 ${h.link_to(_('admin'),h.url('admin_home'),class_='admin')}
229 ${h.link_to(_('admin'),h.url('admin_home'),class_='admin')}
230 <%def name="admin_menu()">
230 <%def name="admin_menu()">
231 <ul>
231 <ul>
232 <li>${h.link_to(_('journal'),h.url('admin_home'),class_='journal')}</li>
232 <li>${h.link_to(_('journal'),h.url('admin_home'),class_='journal')}</li>
233 <li>${h.link_to(_('repositories'),h.url('repos'),class_='repos')}</li>
233 <li>${h.link_to(_('repositories'),h.url('repos'),class_='repos')}</li>
234 <li>${h.link_to(_('repositories groups'),h.url('repos_groups'),class_='repos_groups')}</li>
234 <li>${h.link_to(_('repositories groups'),h.url('repos_groups'),class_='repos_groups')}</li>
235 <li>${h.link_to(_('users'),h.url('users'),class_='users')}</li>
235 <li>${h.link_to(_('users'),h.url('users'),class_='users')}</li>
236 <li>${h.link_to(_('users groups'),h.url('users_groups'),class_='groups')}</li>
236 <li>${h.link_to(_('users groups'),h.url('users_groups'),class_='groups')}</li>
237 <li>${h.link_to(_('permissions'),h.url('edit_permission',id='default'),class_='permissions')}</li>
237 <li>${h.link_to(_('permissions'),h.url('edit_permission',id='default'),class_='permissions')}</li>
238 <li>${h.link_to(_('ldap'),h.url('ldap_home'),class_='ldap')}</li>
238 <li>${h.link_to(_('ldap'),h.url('ldap_home'),class_='ldap')}</li>
239 <li>${h.link_to(_('defaults'),h.url('defaults'),class_='defaults')}</li>
239 <li class="last">${h.link_to(_('settings'),h.url('admin_settings'),class_='settings')}</li>
240 <li class="last">${h.link_to(_('settings'),h.url('admin_settings'),class_='settings')}</li>
240 </ul>
241 </ul>
241 </%def>
242 </%def>
242 ## ADMIN MENU
243 ## ADMIN MENU
243 ${admin_menu()}
244 ${admin_menu()}
244 </li>
245 </li>
245 % endif
246 % endif
246 </ul>
247 </ul>
247 </li>
248 </li>
248
249
249 <li>
250 <li>
250 <a class="menu_link" title="${_('Followers')}" href="${h.url('repo_followers_home',repo_name=c.repo_name)}">
251 <a class="menu_link" title="${_('Followers')}" href="${h.url('repo_followers_home',repo_name=c.repo_name)}">
251 <span class="icon_short">
252 <span class="icon_short">
252 <img src="${h.url('/images/icons/heart.png')}" alt="${_('Followers')}" />
253 <img src="${h.url('/images/icons/heart.png')}" alt="${_('Followers')}" />
253 </span>
254 </span>
254 <span id="current_followers_count" class="short">${c.repository_followers}</span>
255 <span id="current_followers_count" class="short">${c.repository_followers}</span>
255 </a>
256 </a>
256 </li>
257 </li>
257 <li>
258 <li>
258 <a class="menu_link" title="${_('Forks')}" href="${h.url('repo_forks_home',repo_name=c.repo_name)}">
259 <a class="menu_link" title="${_('Forks')}" href="${h.url('repo_forks_home',repo_name=c.repo_name)}">
259 <span class="icon_short">
260 <span class="icon_short">
260 <img src="${h.url('/images/icons/arrow_divide.png')}" alt="${_('Forks')}" />
261 <img src="${h.url('/images/icons/arrow_divide.png')}" alt="${_('Forks')}" />
261 </span>
262 </span>
262 <span class="short">${c.repository_forks}</span>
263 <span class="short">${c.repository_forks}</span>
263 </a>
264 </a>
264 </li>
265 </li>
265 <li>
266 <li>
266 <a class="menu_link" title="${_('Pull requests')}" href="${h.url('pullrequest_show_all',repo_name=c.repo_name)}">
267 <a class="menu_link" title="${_('Pull requests')}" href="${h.url('pullrequest_show_all',repo_name=c.repo_name)}">
267 <span class="icon_short">
268 <span class="icon_short">
268 <img src="${h.url('/images/icons/arrow_join.png')}" alt="${_('Pull requests')}" />
269 <img src="${h.url('/images/icons/arrow_join.png')}" alt="${_('Pull requests')}" />
269 </span>
270 </span>
270 <span class="short">${c.repository_pull_requests}</span>
271 <span class="short">${c.repository_pull_requests}</span>
271 </a>
272 </a>
272 </li>
273 </li>
273 ${usermenu()}
274 ${usermenu()}
274 </ul>
275 </ul>
275 <script type="text/javascript">
276 <script type="text/javascript">
276 YUE.on('repo_switcher','mouseover',function(){
277 YUE.on('repo_switcher','mouseover',function(){
277 function qfilter(){
278 function qfilter(){
278 var nodes = YUQ('ul#repo_switcher_list li a.repo_name');
279 var nodes = YUQ('ul#repo_switcher_list li a.repo_name');
279 var target = 'q_filter_rs';
280 var target = 'q_filter_rs';
280 var func = function(node){
281 var func = function(node){
281 return node.parentNode;
282 return node.parentNode;
282 }
283 }
283 q_filter(target,nodes,func);
284 q_filter(target,nodes,func);
284 }
285 }
285 var loaded = YUD.hasClass('repo_switcher','loaded');
286 var loaded = YUD.hasClass('repo_switcher','loaded');
286 if(!loaded){
287 if(!loaded){
287 YUD.addClass('repo_switcher','loaded');
288 YUD.addClass('repo_switcher','loaded');
288 ypjax("${h.url('repo_switcher')}",'repo_switcher_list',
289 ypjax("${h.url('repo_switcher')}",'repo_switcher_list',
289 function(o){qfilter();},
290 function(o){qfilter();},
290 function(o){YUD.removeClass('repo_switcher','loaded');}
291 function(o){YUD.removeClass('repo_switcher','loaded');}
291 ,null);
292 ,null);
292 }
293 }
293 return false;
294 return false;
294 });
295 });
295
296
296 YUE.on('branch_tag_switcher','mouseover',function(){
297 YUE.on('branch_tag_switcher','mouseover',function(){
297 var loaded = YUD.hasClass('branch_tag_switcher','loaded');
298 var loaded = YUD.hasClass('branch_tag_switcher','loaded');
298 if(!loaded){
299 if(!loaded){
299 YUD.addClass('branch_tag_switcher','loaded');
300 YUD.addClass('branch_tag_switcher','loaded');
300 ypjax("${h.url('branch_tag_switcher',repo_name=c.repo_name)}",'switch_to_list',
301 ypjax("${h.url('branch_tag_switcher',repo_name=c.repo_name)}",'switch_to_list',
301 function(o){},
302 function(o){},
302 function(o){YUD.removeClass('branch_tag_switcher','loaded');}
303 function(o){YUD.removeClass('branch_tag_switcher','loaded');}
303 ,null);
304 ,null);
304 }
305 }
305 return false;
306 return false;
306 });
307 });
307 </script>
308 </script>
308 %else:
309 %else:
309 ##ROOT MENU
310 ##ROOT MENU
310 <ul id="quick">
311 <ul id="quick">
311 <li>
312 <li>
312 <a class="menu_link" title="${_('Home')}" href="${h.url('home')}">
313 <a class="menu_link" title="${_('Home')}" href="${h.url('home')}">
313 <span class="icon">
314 <span class="icon">
314 <img src="${h.url('/images/icons/home_16.png')}" alt="${_('Home')}" />
315 <img src="${h.url('/images/icons/home_16.png')}" alt="${_('Home')}" />
315 </span>
316 </span>
316 <span>${_('Home')}</span>
317 <span>${_('Home')}</span>
317 </a>
318 </a>
318 </li>
319 </li>
319 %if c.rhodecode_user.username != 'default':
320 %if c.rhodecode_user.username != 'default':
320 <li>
321 <li>
321 <a class="menu_link" title="${_('Journal')}" href="${h.url('journal')}">
322 <a class="menu_link" title="${_('Journal')}" href="${h.url('journal')}">
322 <span class="icon">
323 <span class="icon">
323 <img src="${h.url('/images/icons/book.png')}" alt="${_('Journal')}" />
324 <img src="${h.url('/images/icons/book.png')}" alt="${_('Journal')}" />
324 </span>
325 </span>
325 <span>${_('Journal')}</span>
326 <span>${_('Journal')}</span>
326 </a>
327 </a>
327 </li>
328 </li>
328 %else:
329 %else:
329 <li>
330 <li>
330 <a class="menu_link" title="${_('Public journal')}" href="${h.url('public_journal')}">
331 <a class="menu_link" title="${_('Public journal')}" href="${h.url('public_journal')}">
331 <span class="icon">
332 <span class="icon">
332 <img src="${h.url('/images/icons/book.png')}" alt="${_('Public journal')}" />
333 <img src="${h.url('/images/icons/book.png')}" alt="${_('Public journal')}" />
333 </span>
334 </span>
334 <span>${_('Public journal')}</span>
335 <span>${_('Public journal')}</span>
335 </a>
336 </a>
336 </li>
337 </li>
337 %endif
338 %endif
338 <li>
339 <li>
339 <a class="menu_link" title="${_('Search')}" href="${h.url('search')}">
340 <a class="menu_link" title="${_('Search')}" href="${h.url('search')}">
340 <span class="icon">
341 <span class="icon">
341 <img src="${h.url('/images/icons/search_16.png')}" alt="${_('Search')}" />
342 <img src="${h.url('/images/icons/search_16.png')}" alt="${_('Search')}" />
342 </span>
343 </span>
343 <span>${_('Search')}</span>
344 <span>${_('Search')}</span>
344 </a>
345 </a>
345 </li>
346 </li>
346
347
347 %if h.HasPermissionAll('hg.admin')('access admin main page'):
348 %if h.HasPermissionAll('hg.admin')('access admin main page'):
348 <li ${is_current('admin')}>
349 <li ${is_current('admin')}>
349 <a class="menu_link" title="${_('Admin')}" href="${h.url('admin_home')}">
350 <a class="menu_link" title="${_('Admin')}" href="${h.url('admin_home')}">
350 <span class="icon">
351 <span class="icon">
351 <img src="${h.url('/images/icons/cog_edit.png')}" alt="${_('Admin')}" />
352 <img src="${h.url('/images/icons/cog_edit.png')}" alt="${_('Admin')}" />
352 </span>
353 </span>
353 <span>${_('Admin')}</span>
354 <span>${_('Admin')}</span>
354 </a>
355 </a>
355 ${admin_menu()}
356 ${admin_menu()}
356 </li>
357 </li>
357 %endif
358 %endif
358 ${usermenu()}
359 ${usermenu()}
359 </ul>
360 </ul>
360 %endif
361 %endif
361 </%def>
362 </%def>
@@ -1,165 +1,186 b''
1 """Pylons application test package
1 """Pylons application test package
2
2
3 This package assumes the Pylons environment is already loaded, such as
3 This package assumes the Pylons environment is already loaded, such as
4 when this script is imported from the `nosetests --with-pylons=test.ini`
4 when this script is imported from the `nosetests --with-pylons=test.ini`
5 command.
5 command.
6
6
7 This module initializes the application via ``websetup`` (`paster
7 This module initializes the application via ``websetup`` (`paster
8 setup-app`) and provides the base testing objects.
8 setup-app`) and provides the base testing objects.
9 """
9 """
10 import os
10 import os
11 import time
11 import time
12 import logging
12 import logging
13 import datetime
13 import datetime
14 import hashlib
14 import hashlib
15 import tempfile
15 import tempfile
16 from os.path import join as jn
16 from os.path import join as jn
17
17
18 from unittest import TestCase
18 from unittest import TestCase
19 from tempfile import _RandomNameSequence
19 from tempfile import _RandomNameSequence
20
20
21 from paste.deploy import loadapp
21 from paste.deploy import loadapp
22 from paste.script.appinstall import SetupCommand
22 from paste.script.appinstall import SetupCommand
23 from pylons import config, url
23 from pylons import config, url
24 from routes.util import URLGenerator
24 from routes.util import URLGenerator
25 from webtest import TestApp
25 from webtest import TestApp
26
26
27 from rhodecode import is_windows
27 from rhodecode import is_windows
28 from rhodecode.model.meta import Session
28 from rhodecode.model.meta import Session
29 from rhodecode.model.db import User
29 from rhodecode.model.db import User
30 from rhodecode.tests.nose_parametrized import parameterized
30 from rhodecode.tests.nose_parametrized import parameterized
31
31
32 import pylons.test
32 import pylons.test
33
33
34
34
35 os.environ['TZ'] = 'UTC'
35 os.environ['TZ'] = 'UTC'
36 if not is_windows:
36 if not is_windows:
37 time.tzset()
37 time.tzset()
38
38
39 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
40
40
41 __all__ = [
41 __all__ = [
42 'parameterized', 'environ', 'url', 'get_new_dir', 'TestController',
42 'parameterized', 'environ', 'url', 'get_new_dir', 'TestController',
43 'TESTS_TMP_PATH', 'HG_REPO', 'GIT_REPO', 'NEW_HG_REPO', 'NEW_GIT_REPO',
43 'TESTS_TMP_PATH', 'HG_REPO', 'GIT_REPO', 'NEW_HG_REPO', 'NEW_GIT_REPO',
44 'HG_FORK', 'GIT_FORK', 'TEST_USER_ADMIN_LOGIN', 'TEST_USER_ADMIN_PASS',
44 'HG_FORK', 'GIT_FORK', 'TEST_USER_ADMIN_LOGIN', 'TEST_USER_ADMIN_PASS',
45 'TEST_USER_REGULAR_LOGIN', 'TEST_USER_REGULAR_PASS',
45 'TEST_USER_REGULAR_LOGIN', 'TEST_USER_REGULAR_PASS',
46 'TEST_USER_REGULAR_EMAIL', 'TEST_USER_REGULAR2_LOGIN',
46 'TEST_USER_REGULAR_EMAIL', 'TEST_USER_REGULAR2_LOGIN',
47 'TEST_USER_REGULAR2_PASS', 'TEST_USER_REGULAR2_EMAIL', 'TEST_HG_REPO',
47 'TEST_USER_REGULAR2_PASS', 'TEST_USER_REGULAR2_EMAIL', 'TEST_HG_REPO',
48 'TEST_HG_REPO_CLONE', 'TEST_HG_REPO_PULL', 'TEST_GIT_REPO',
48 'TEST_HG_REPO_CLONE', 'TEST_HG_REPO_PULL', 'TEST_GIT_REPO',
49 'TEST_GIT_REPO_CLONE', 'TEST_GIT_REPO_PULL', 'HG_REMOTE_REPO',
49 'TEST_GIT_REPO_CLONE', 'TEST_GIT_REPO_PULL', 'HG_REMOTE_REPO',
50 'GIT_REMOTE_REPO', 'SCM_TESTS',
50 'GIT_REMOTE_REPO', 'SCM_TESTS', '_get_repo_create_params'
51 ]
51 ]
52
52
53 # Invoke websetup with the current config file
53 # Invoke websetup with the current config file
54 # SetupCommand('setup-app').run([config_file])
54 # SetupCommand('setup-app').run([config_file])
55
55
56 ##RUNNING DESIRED TESTS
56 ##RUNNING DESIRED TESTS
57 # nosetests -x rhodecode.tests.functional.test_admin_settings:TestSettingsController.test_my_account
57 # nosetests -x rhodecode.tests.functional.test_admin_settings:TestSettingsController.test_my_account
58 # nosetests --pdb --pdb-failures
58 # nosetests --pdb --pdb-failures
59 # nosetests --with-coverage --cover-package=rhodecode.model.validators rhodecode.tests.test_validators
59 # nosetests --with-coverage --cover-package=rhodecode.model.validators rhodecode.tests.test_validators
60 environ = {}
60 environ = {}
61
61
62 #SOME GLOBALS FOR TESTS
62 #SOME GLOBALS FOR TESTS
63
63
64 TESTS_TMP_PATH = jn('/', 'tmp', 'rc_test_%s' % _RandomNameSequence().next())
64 TESTS_TMP_PATH = jn('/', 'tmp', 'rc_test_%s' % _RandomNameSequence().next())
65 TEST_USER_ADMIN_LOGIN = 'test_admin'
65 TEST_USER_ADMIN_LOGIN = 'test_admin'
66 TEST_USER_ADMIN_PASS = 'test12'
66 TEST_USER_ADMIN_PASS = 'test12'
67 TEST_USER_ADMIN_EMAIL = 'test_admin@mail.com'
67 TEST_USER_ADMIN_EMAIL = 'test_admin@mail.com'
68
68
69 TEST_USER_REGULAR_LOGIN = 'test_regular'
69 TEST_USER_REGULAR_LOGIN = 'test_regular'
70 TEST_USER_REGULAR_PASS = 'test12'
70 TEST_USER_REGULAR_PASS = 'test12'
71 TEST_USER_REGULAR_EMAIL = 'test_regular@mail.com'
71 TEST_USER_REGULAR_EMAIL = 'test_regular@mail.com'
72
72
73 TEST_USER_REGULAR2_LOGIN = 'test_regular2'
73 TEST_USER_REGULAR2_LOGIN = 'test_regular2'
74 TEST_USER_REGULAR2_PASS = 'test12'
74 TEST_USER_REGULAR2_PASS = 'test12'
75 TEST_USER_REGULAR2_EMAIL = 'test_regular2@mail.com'
75 TEST_USER_REGULAR2_EMAIL = 'test_regular2@mail.com'
76
76
77 HG_REPO = 'vcs_test_hg'
77 HG_REPO = 'vcs_test_hg'
78 GIT_REPO = 'vcs_test_git'
78 GIT_REPO = 'vcs_test_git'
79
79
80 NEW_HG_REPO = 'vcs_test_hg_new'
80 NEW_HG_REPO = 'vcs_test_hg_new'
81 NEW_GIT_REPO = 'vcs_test_git_new'
81 NEW_GIT_REPO = 'vcs_test_git_new'
82
82
83 HG_FORK = 'vcs_test_hg_fork'
83 HG_FORK = 'vcs_test_hg_fork'
84 GIT_FORK = 'vcs_test_git_fork'
84 GIT_FORK = 'vcs_test_git_fork'
85
85
86 ## VCS
86 ## VCS
87 SCM_TESTS = ['hg', 'git']
87 SCM_TESTS = ['hg', 'git']
88 uniq_suffix = str(int(time.mktime(datetime.datetime.now().timetuple())))
88 uniq_suffix = str(int(time.mktime(datetime.datetime.now().timetuple())))
89
89
90 GIT_REMOTE_REPO = 'git://github.com/codeinn/vcs.git'
90 GIT_REMOTE_REPO = 'git://github.com/codeinn/vcs.git'
91
91
92 TEST_GIT_REPO = jn(TESTS_TMP_PATH, GIT_REPO)
92 TEST_GIT_REPO = jn(TESTS_TMP_PATH, GIT_REPO)
93 TEST_GIT_REPO_CLONE = jn(TESTS_TMP_PATH, 'vcsgitclone%s' % uniq_suffix)
93 TEST_GIT_REPO_CLONE = jn(TESTS_TMP_PATH, 'vcsgitclone%s' % uniq_suffix)
94 TEST_GIT_REPO_PULL = jn(TESTS_TMP_PATH, 'vcsgitpull%s' % uniq_suffix)
94 TEST_GIT_REPO_PULL = jn(TESTS_TMP_PATH, 'vcsgitpull%s' % uniq_suffix)
95
95
96
96
97 HG_REMOTE_REPO = 'http://bitbucket.org/marcinkuzminski/vcs'
97 HG_REMOTE_REPO = 'http://bitbucket.org/marcinkuzminski/vcs'
98
98
99 TEST_HG_REPO = jn(TESTS_TMP_PATH, HG_REPO)
99 TEST_HG_REPO = jn(TESTS_TMP_PATH, HG_REPO)
100 TEST_HG_REPO_CLONE = jn(TESTS_TMP_PATH, 'vcshgclone%s' % uniq_suffix)
100 TEST_HG_REPO_CLONE = jn(TESTS_TMP_PATH, 'vcshgclone%s' % uniq_suffix)
101 TEST_HG_REPO_PULL = jn(TESTS_TMP_PATH, 'vcshgpull%s' % uniq_suffix)
101 TEST_HG_REPO_PULL = jn(TESTS_TMP_PATH, 'vcshgpull%s' % uniq_suffix)
102
102
103 TEST_DIR = tempfile.gettempdir()
103 TEST_DIR = tempfile.gettempdir()
104 TEST_REPO_PREFIX = 'vcs-test'
104 TEST_REPO_PREFIX = 'vcs-test'
105
105
106 # cached repos if any !
106 # cached repos if any !
107 # comment out to get some other repos from bb or github
107 # comment out to get some other repos from bb or github
108 GIT_REMOTE_REPO = jn(TESTS_TMP_PATH, GIT_REPO)
108 GIT_REMOTE_REPO = jn(TESTS_TMP_PATH, GIT_REPO)
109 HG_REMOTE_REPO = jn(TESTS_TMP_PATH, HG_REPO)
109 HG_REMOTE_REPO = jn(TESTS_TMP_PATH, HG_REPO)
110
110
111
111
112 def get_new_dir(title):
112 def get_new_dir(title):
113 """
113 """
114 Returns always new directory path.
114 Returns always new directory path.
115 """
115 """
116 from rhodecode.tests.vcs.utils import get_normalized_path
116 from rhodecode.tests.vcs.utils import get_normalized_path
117 name = TEST_REPO_PREFIX
117 name = TEST_REPO_PREFIX
118 if title:
118 if title:
119 name = '-'.join((name, title))
119 name = '-'.join((name, title))
120 hex = hashlib.sha1(str(time.time())).hexdigest()
120 hex = hashlib.sha1(str(time.time())).hexdigest()
121 name = '-'.join((name, hex))
121 name = '-'.join((name, hex))
122 path = os.path.join(TEST_DIR, name)
122 path = os.path.join(TEST_DIR, name)
123 return get_normalized_path(path)
123 return get_normalized_path(path)
124
124
125
125
126 class TestController(TestCase):
126 class TestController(TestCase):
127
127
128 def __init__(self, *args, **kwargs):
128 def __init__(self, *args, **kwargs):
129 wsgiapp = pylons.test.pylonsapp
129 wsgiapp = pylons.test.pylonsapp
130 config = wsgiapp.config
130 config = wsgiapp.config
131
131
132 self.app = TestApp(wsgiapp)
132 self.app = TestApp(wsgiapp)
133 url._push_object(URLGenerator(config['routes.map'], environ))
133 url._push_object(URLGenerator(config['routes.map'], environ))
134 self.Session = Session
134 self.Session = Session
135 self.index_location = config['app_conf']['index_dir']
135 self.index_location = config['app_conf']['index_dir']
136 TestCase.__init__(self, *args, **kwargs)
136 TestCase.__init__(self, *args, **kwargs)
137
137
138 def log_user(self, username=TEST_USER_ADMIN_LOGIN,
138 def log_user(self, username=TEST_USER_ADMIN_LOGIN,
139 password=TEST_USER_ADMIN_PASS):
139 password=TEST_USER_ADMIN_PASS):
140 self._logged_username = username
140 self._logged_username = username
141 response = self.app.post(url(controller='login', action='index'),
141 response = self.app.post(url(controller='login', action='index'),
142 {'username': username,
142 {'username': username,
143 'password': password})
143 'password': password})
144
144
145 if 'invalid user name' in response.body:
145 if 'invalid user name' in response.body:
146 self.fail('could not login using %s %s' % (username, password))
146 self.fail('could not login using %s %s' % (username, password))
147
147
148 self.assertEqual(response.status, '302 Found')
148 self.assertEqual(response.status, '302 Found')
149 ses = response.session['rhodecode_user']
149 ses = response.session['rhodecode_user']
150 self.assertEqual(ses.get('username'), username)
150 self.assertEqual(ses.get('username'), username)
151 response = response.follow()
151 response = response.follow()
152 self.assertEqual(ses.get('is_authenticated'), True)
152 self.assertEqual(ses.get('is_authenticated'), True)
153
153
154 return response.session['rhodecode_user']
154 return response.session['rhodecode_user']
155
155
156 def _get_logged_user(self):
156 def _get_logged_user(self):
157 return User.get_by_username(self._logged_username)
157 return User.get_by_username(self._logged_username)
158
158
159 def checkSessionFlash(self, response, msg):
159 def checkSessionFlash(self, response, msg):
160 self.assertTrue('flash' in response.session)
160 self.assertTrue('flash' in response.session)
161 if not msg in response.session['flash'][0][1]:
161 if not msg in response.session['flash'][0][1]:
162 self.fail(
162 self.fail(
163 'msg `%s` not found in session flash: got `%s` instead' % (
163 'msg `%s` not found in session flash: got `%s` instead' % (
164 msg, response.session['flash'])
164 msg, response.session['flash'])
165 )
165 )
166
167
168 ## HELPERS ##
169
170 def _get_repo_create_params(**custom):
171 defs = {
172 'repo_name': None,
173 'repo_type': 'hg',
174 'clone_uri': '',
175 'repo_group': '',
176 'repo_description': 'DESC',
177 'repo_private': False,
178 'repo_landing_rev': 'tip'
179 }
180 defs.update(custom)
181 if 'repo_name_full' not in custom:
182 defs.update({'repo_name_full': defs['repo_name']})
183
184 return defs
185
186
@@ -1,990 +1,985 b''
1 from __future__ import with_statement
1 from __future__ import with_statement
2 import random
2 import random
3 import mock
3 import mock
4
4
5 from rhodecode.tests import *
5 from rhodecode.tests import *
6 from rhodecode.lib.compat import json
6 from rhodecode.lib.compat import json
7 from rhodecode.lib.auth import AuthUser
7 from rhodecode.lib.auth import AuthUser
8 from rhodecode.model.user import UserModel
8 from rhodecode.model.user import UserModel
9 from rhodecode.model.users_group import UsersGroupModel
9 from rhodecode.model.users_group import UsersGroupModel
10 from rhodecode.model.repo import RepoModel
10 from rhodecode.model.repo import RepoModel
11 from rhodecode.model.meta import Session
11 from rhodecode.model.meta import Session
12 from rhodecode.model.scm import ScmModel
12 from rhodecode.model.scm import ScmModel
13 from rhodecode.model.db import Repository
13 from rhodecode.model.db import Repository
14
14
15 API_URL = '/_admin/api'
15 API_URL = '/_admin/api'
16
16
17
17
18 def _build_data(apikey, method, **kw):
18 def _build_data(apikey, method, **kw):
19 """
19 """
20 Builds API data with given random ID
20 Builds API data with given random ID
21
21
22 :param random_id:
22 :param random_id:
23 :type random_id:
23 :type random_id:
24 """
24 """
25 random_id = random.randrange(1, 9999)
25 random_id = random.randrange(1, 9999)
26 return random_id, json.dumps({
26 return random_id, json.dumps({
27 "id": random_id,
27 "id": random_id,
28 "api_key": apikey,
28 "api_key": apikey,
29 "method": method,
29 "method": method,
30 "args": kw
30 "args": kw
31 })
31 })
32
32
33 jsonify = lambda obj: json.loads(json.dumps(obj))
33 jsonify = lambda obj: json.loads(json.dumps(obj))
34
34
35
35
36 def crash(*args, **kwargs):
36 def crash(*args, **kwargs):
37 raise Exception('Total Crash !')
37 raise Exception('Total Crash !')
38
38
39
39
40 def api_call(test_obj, params):
40 def api_call(test_obj, params):
41 response = test_obj.app.post(API_URL, content_type='application/json',
41 response = test_obj.app.post(API_URL, content_type='application/json',
42 params=params)
42 params=params)
43 return response
43 return response
44
44
45
45
46 TEST_USERS_GROUP = 'test_users_group'
46 TEST_USERS_GROUP = 'test_users_group'
47
47
48
48
49 def make_users_group(name=TEST_USERS_GROUP):
49 def make_users_group(name=TEST_USERS_GROUP):
50 gr = UsersGroupModel().create(name=name)
50 gr = UsersGroupModel().create(name=name)
51 UsersGroupModel().add_user_to_group(users_group=gr,
51 UsersGroupModel().add_user_to_group(users_group=gr,
52 user=TEST_USER_ADMIN_LOGIN)
52 user=TEST_USER_ADMIN_LOGIN)
53 Session().commit()
53 Session().commit()
54 return gr
54 return gr
55
55
56
56
57 def destroy_users_group(name=TEST_USERS_GROUP):
57 def destroy_users_group(name=TEST_USERS_GROUP):
58 UsersGroupModel().delete(users_group=name, force=True)
58 UsersGroupModel().delete(users_group=name, force=True)
59 Session().commit()
59 Session().commit()
60
60
61
61
62 def create_repo(repo_name, repo_type):
62 def create_repo(repo_name, repo_type):
63 # create new repo
63 # create new repo
64 form_data = dict(repo_name=repo_name,
64 form_data = _get_repo_create_params(
65 repo_name_full=repo_name,
65 repo_name_full=repo_name,
66 fork_name=None,
66 repo_description='description %s' % repo_name,
67 description='description %s' % repo_name,
67 )
68 repo_group=None,
69 private=False,
70 repo_type=repo_type,
71 clone_uri=None,
72 landing_rev='tip')
73 cur_user = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
68 cur_user = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
74 r = RepoModel().create(form_data, cur_user)
69 r = RepoModel().create(form_data, cur_user)
75 Session().commit()
70 Session().commit()
76 return r
71 return r
77
72
78
73
79 def create_fork(fork_name, fork_type, fork_of):
74 def create_fork(fork_name, fork_type, fork_of):
80 fork = RepoModel(Session())._get_repo(fork_of)
75 fork = RepoModel(Session())._get_repo(fork_of)
81 r = create_repo(fork_name, fork_type)
76 r = create_repo(fork_name, fork_type)
82 r.fork = fork
77 r.fork = fork
83 Session().add(r)
78 Session().add(r)
84 Session().commit()
79 Session().commit()
85 return r
80 return r
86
81
87
82
88 def destroy_repo(repo_name):
83 def destroy_repo(repo_name):
89 RepoModel().delete(repo_name)
84 RepoModel().delete(repo_name)
90 Session().commit()
85 Session().commit()
91
86
92
87
93 class BaseTestApi(object):
88 class BaseTestApi(object):
94 REPO = None
89 REPO = None
95 REPO_TYPE = None
90 REPO_TYPE = None
96
91
97 @classmethod
92 @classmethod
98 def setUpClass(self):
93 def setUpClass(self):
99 self.usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
94 self.usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
100 self.apikey = self.usr.api_key
95 self.apikey = self.usr.api_key
101 self.TEST_USER = UserModel().create_or_update(
96 self.TEST_USER = UserModel().create_or_update(
102 username='test-api',
97 username='test-api',
103 password='test',
98 password='test',
104 email='test@api.rhodecode.org',
99 email='test@api.rhodecode.org',
105 firstname='first',
100 firstname='first',
106 lastname='last'
101 lastname='last'
107 )
102 )
108 Session().commit()
103 Session().commit()
109 self.TEST_USER_LOGIN = self.TEST_USER.username
104 self.TEST_USER_LOGIN = self.TEST_USER.username
110
105
111 @classmethod
106 @classmethod
112 def teardownClass(self):
107 def teardownClass(self):
113 pass
108 pass
114
109
115 def setUp(self):
110 def setUp(self):
116 self.maxDiff = None
111 self.maxDiff = None
117 make_users_group()
112 make_users_group()
118
113
119 def tearDown(self):
114 def tearDown(self):
120 destroy_users_group()
115 destroy_users_group()
121
116
122 def _compare_ok(self, id_, expected, given):
117 def _compare_ok(self, id_, expected, given):
123 expected = jsonify({
118 expected = jsonify({
124 'id': id_,
119 'id': id_,
125 'error': None,
120 'error': None,
126 'result': expected
121 'result': expected
127 })
122 })
128 given = json.loads(given)
123 given = json.loads(given)
129 self.assertEqual(expected, given)
124 self.assertEqual(expected, given)
130
125
131 def _compare_error(self, id_, expected, given):
126 def _compare_error(self, id_, expected, given):
132 expected = jsonify({
127 expected = jsonify({
133 'id': id_,
128 'id': id_,
134 'error': expected,
129 'error': expected,
135 'result': None
130 'result': None
136 })
131 })
137 given = json.loads(given)
132 given = json.loads(given)
138 self.assertEqual(expected, given)
133 self.assertEqual(expected, given)
139
134
140 # def test_Optional(self):
135 # def test_Optional(self):
141 # from rhodecode.controllers.api.api import Optional
136 # from rhodecode.controllers.api.api import Optional
142 # option1 = Optional(None)
137 # option1 = Optional(None)
143 # self.assertEqual('<Optional:%s>' % None, repr(option1))
138 # self.assertEqual('<Optional:%s>' % None, repr(option1))
144 #
139 #
145 # self.assertEqual(1, Optional.extract(Optional(1)))
140 # self.assertEqual(1, Optional.extract(Optional(1)))
146 # self.assertEqual('trololo', Optional.extract('trololo'))
141 # self.assertEqual('trololo', Optional.extract('trololo'))
147
142
148 def test_api_wrong_key(self):
143 def test_api_wrong_key(self):
149 id_, params = _build_data('trololo', 'get_user')
144 id_, params = _build_data('trololo', 'get_user')
150 response = api_call(self, params)
145 response = api_call(self, params)
151
146
152 expected = 'Invalid API KEY'
147 expected = 'Invalid API KEY'
153 self._compare_error(id_, expected, given=response.body)
148 self._compare_error(id_, expected, given=response.body)
154
149
155 def test_api_missing_non_optional_param(self):
150 def test_api_missing_non_optional_param(self):
156 id_, params = _build_data(self.apikey, 'get_user')
151 id_, params = _build_data(self.apikey, 'get_user')
157 response = api_call(self, params)
152 response = api_call(self, params)
158
153
159 expected = 'Missing non optional `userid` arg in JSON DATA'
154 expected = 'Missing non optional `userid` arg in JSON DATA'
160 self._compare_error(id_, expected, given=response.body)
155 self._compare_error(id_, expected, given=response.body)
161
156
162 def test_api_get_users(self):
157 def test_api_get_users(self):
163 id_, params = _build_data(self.apikey, 'get_users',)
158 id_, params = _build_data(self.apikey, 'get_users',)
164 response = api_call(self, params)
159 response = api_call(self, params)
165 ret_all = []
160 ret_all = []
166 for usr in UserModel().get_all():
161 for usr in UserModel().get_all():
167 ret = usr.get_api_data()
162 ret = usr.get_api_data()
168 ret_all.append(jsonify(ret))
163 ret_all.append(jsonify(ret))
169 expected = ret_all
164 expected = ret_all
170 self._compare_ok(id_, expected, given=response.body)
165 self._compare_ok(id_, expected, given=response.body)
171
166
172 def test_api_get_user(self):
167 def test_api_get_user(self):
173 id_, params = _build_data(self.apikey, 'get_user',
168 id_, params = _build_data(self.apikey, 'get_user',
174 userid=TEST_USER_ADMIN_LOGIN)
169 userid=TEST_USER_ADMIN_LOGIN)
175 response = api_call(self, params)
170 response = api_call(self, params)
176
171
177 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
172 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
178 ret = usr.get_api_data()
173 ret = usr.get_api_data()
179 ret['permissions'] = AuthUser(usr.user_id).permissions
174 ret['permissions'] = AuthUser(usr.user_id).permissions
180
175
181 expected = ret
176 expected = ret
182 self._compare_ok(id_, expected, given=response.body)
177 self._compare_ok(id_, expected, given=response.body)
183
178
184 def test_api_get_user_that_does_not_exist(self):
179 def test_api_get_user_that_does_not_exist(self):
185 id_, params = _build_data(self.apikey, 'get_user',
180 id_, params = _build_data(self.apikey, 'get_user',
186 userid='trololo')
181 userid='trololo')
187 response = api_call(self, params)
182 response = api_call(self, params)
188
183
189 expected = "user `%s` does not exist" % 'trololo'
184 expected = "user `%s` does not exist" % 'trololo'
190 self._compare_error(id_, expected, given=response.body)
185 self._compare_error(id_, expected, given=response.body)
191
186
192 def test_api_pull(self):
187 def test_api_pull(self):
193 #TODO: issues with rhodecode_extras here.. not sure why !
188 #TODO: issues with rhodecode_extras here.. not sure why !
194 pass
189 pass
195
190
196 # repo_name = 'test_pull'
191 # repo_name = 'test_pull'
197 # r = create_repo(repo_name, self.REPO_TYPE)
192 # r = create_repo(repo_name, self.REPO_TYPE)
198 # r.clone_uri = TEST_self.REPO
193 # r.clone_uri = TEST_self.REPO
199 # Session.add(r)
194 # Session.add(r)
200 # Session.commit()
195 # Session.commit()
201 #
196 #
202 # id_, params = _build_data(self.apikey, 'pull',
197 # id_, params = _build_data(self.apikey, 'pull',
203 # repoid=repo_name,)
198 # repoid=repo_name,)
204 # response = self.app.post(API_URL, content_type='application/json',
199 # response = self.app.post(API_URL, content_type='application/json',
205 # params=params)
200 # params=params)
206 #
201 #
207 # expected = 'Pulled from `%s`' % repo_name
202 # expected = 'Pulled from `%s`' % repo_name
208 # self._compare_ok(id_, expected, given=response.body)
203 # self._compare_ok(id_, expected, given=response.body)
209 #
204 #
210 # destroy_repo(repo_name)
205 # destroy_repo(repo_name)
211
206
212 def test_api_pull_error(self):
207 def test_api_pull_error(self):
213 id_, params = _build_data(self.apikey, 'pull',
208 id_, params = _build_data(self.apikey, 'pull',
214 repoid=self.REPO,)
209 repoid=self.REPO,)
215 response = api_call(self, params)
210 response = api_call(self, params)
216
211
217 expected = 'Unable to pull changes from `%s`' % self.REPO
212 expected = 'Unable to pull changes from `%s`' % self.REPO
218 self._compare_error(id_, expected, given=response.body)
213 self._compare_error(id_, expected, given=response.body)
219
214
220 def test_api_rescan_repos(self):
215 def test_api_rescan_repos(self):
221 id_, params = _build_data(self.apikey, 'rescan_repos')
216 id_, params = _build_data(self.apikey, 'rescan_repos')
222 response = api_call(self, params)
217 response = api_call(self, params)
223
218
224 expected = {'added': [], 'removed': []}
219 expected = {'added': [], 'removed': []}
225 self._compare_ok(id_, expected, given=response.body)
220 self._compare_ok(id_, expected, given=response.body)
226
221
227 @mock.patch.object(ScmModel, 'repo_scan', crash)
222 @mock.patch.object(ScmModel, 'repo_scan', crash)
228 def test_api_rescann_error(self):
223 def test_api_rescann_error(self):
229 id_, params = _build_data(self.apikey, 'rescan_repos',)
224 id_, params = _build_data(self.apikey, 'rescan_repos',)
230 response = api_call(self, params)
225 response = api_call(self, params)
231
226
232 expected = 'Error occurred during rescan repositories action'
227 expected = 'Error occurred during rescan repositories action'
233 self._compare_error(id_, expected, given=response.body)
228 self._compare_error(id_, expected, given=response.body)
234
229
235 def test_api_lock_repo_lock_aquire(self):
230 def test_api_lock_repo_lock_aquire(self):
236 id_, params = _build_data(self.apikey, 'lock',
231 id_, params = _build_data(self.apikey, 'lock',
237 userid=TEST_USER_ADMIN_LOGIN,
232 userid=TEST_USER_ADMIN_LOGIN,
238 repoid=self.REPO,
233 repoid=self.REPO,
239 locked=True)
234 locked=True)
240 response = api_call(self, params)
235 response = api_call(self, params)
241 expected = ('User `%s` set lock state for repo `%s` to `%s`'
236 expected = ('User `%s` set lock state for repo `%s` to `%s`'
242 % (TEST_USER_ADMIN_LOGIN, self.REPO, True))
237 % (TEST_USER_ADMIN_LOGIN, self.REPO, True))
243 self._compare_ok(id_, expected, given=response.body)
238 self._compare_ok(id_, expected, given=response.body)
244
239
245 def test_api_lock_repo_lock_release(self):
240 def test_api_lock_repo_lock_release(self):
246 id_, params = _build_data(self.apikey, 'lock',
241 id_, params = _build_data(self.apikey, 'lock',
247 userid=TEST_USER_ADMIN_LOGIN,
242 userid=TEST_USER_ADMIN_LOGIN,
248 repoid=self.REPO,
243 repoid=self.REPO,
249 locked=False)
244 locked=False)
250 response = api_call(self, params)
245 response = api_call(self, params)
251 expected = ('User `%s` set lock state for repo `%s` to `%s`'
246 expected = ('User `%s` set lock state for repo `%s` to `%s`'
252 % (TEST_USER_ADMIN_LOGIN, self.REPO, False))
247 % (TEST_USER_ADMIN_LOGIN, self.REPO, False))
253 self._compare_ok(id_, expected, given=response.body)
248 self._compare_ok(id_, expected, given=response.body)
254
249
255 @mock.patch.object(Repository, 'lock', crash)
250 @mock.patch.object(Repository, 'lock', crash)
256 def test_api_lock_error(self):
251 def test_api_lock_error(self):
257 id_, params = _build_data(self.apikey, 'lock',
252 id_, params = _build_data(self.apikey, 'lock',
258 userid=TEST_USER_ADMIN_LOGIN,
253 userid=TEST_USER_ADMIN_LOGIN,
259 repoid=self.REPO,
254 repoid=self.REPO,
260 locked=True)
255 locked=True)
261 response = api_call(self, params)
256 response = api_call(self, params)
262
257
263 expected = 'Error occurred locking repository `%s`' % self.REPO
258 expected = 'Error occurred locking repository `%s`' % self.REPO
264 self._compare_error(id_, expected, given=response.body)
259 self._compare_error(id_, expected, given=response.body)
265
260
266 def test_api_create_existing_user(self):
261 def test_api_create_existing_user(self):
267 id_, params = _build_data(self.apikey, 'create_user',
262 id_, params = _build_data(self.apikey, 'create_user',
268 username=TEST_USER_ADMIN_LOGIN,
263 username=TEST_USER_ADMIN_LOGIN,
269 email='test@foo.com',
264 email='test@foo.com',
270 password='trololo')
265 password='trololo')
271 response = api_call(self, params)
266 response = api_call(self, params)
272
267
273 expected = "user `%s` already exist" % TEST_USER_ADMIN_LOGIN
268 expected = "user `%s` already exist" % TEST_USER_ADMIN_LOGIN
274 self._compare_error(id_, expected, given=response.body)
269 self._compare_error(id_, expected, given=response.body)
275
270
276 def test_api_create_user_with_existing_email(self):
271 def test_api_create_user_with_existing_email(self):
277 id_, params = _build_data(self.apikey, 'create_user',
272 id_, params = _build_data(self.apikey, 'create_user',
278 username=TEST_USER_ADMIN_LOGIN + 'new',
273 username=TEST_USER_ADMIN_LOGIN + 'new',
279 email=TEST_USER_REGULAR_EMAIL,
274 email=TEST_USER_REGULAR_EMAIL,
280 password='trololo')
275 password='trololo')
281 response = api_call(self, params)
276 response = api_call(self, params)
282
277
283 expected = "email `%s` already exist" % TEST_USER_REGULAR_EMAIL
278 expected = "email `%s` already exist" % TEST_USER_REGULAR_EMAIL
284 self._compare_error(id_, expected, given=response.body)
279 self._compare_error(id_, expected, given=response.body)
285
280
286 def test_api_create_user(self):
281 def test_api_create_user(self):
287 username = 'test_new_api_user'
282 username = 'test_new_api_user'
288 email = username + "@foo.com"
283 email = username + "@foo.com"
289
284
290 id_, params = _build_data(self.apikey, 'create_user',
285 id_, params = _build_data(self.apikey, 'create_user',
291 username=username,
286 username=username,
292 email=email,
287 email=email,
293 password='trololo')
288 password='trololo')
294 response = api_call(self, params)
289 response = api_call(self, params)
295
290
296 usr = UserModel().get_by_username(username)
291 usr = UserModel().get_by_username(username)
297 ret = dict(
292 ret = dict(
298 msg='created new user `%s`' % username,
293 msg='created new user `%s`' % username,
299 user=jsonify(usr.get_api_data())
294 user=jsonify(usr.get_api_data())
300 )
295 )
301
296
302 expected = ret
297 expected = ret
303 self._compare_ok(id_, expected, given=response.body)
298 self._compare_ok(id_, expected, given=response.body)
304
299
305 UserModel().delete(usr.user_id)
300 UserModel().delete(usr.user_id)
306 Session().commit()
301 Session().commit()
307
302
308 @mock.patch.object(UserModel, 'create_or_update', crash)
303 @mock.patch.object(UserModel, 'create_or_update', crash)
309 def test_api_create_user_when_exception_happened(self):
304 def test_api_create_user_when_exception_happened(self):
310
305
311 username = 'test_new_api_user'
306 username = 'test_new_api_user'
312 email = username + "@foo.com"
307 email = username + "@foo.com"
313
308
314 id_, params = _build_data(self.apikey, 'create_user',
309 id_, params = _build_data(self.apikey, 'create_user',
315 username=username,
310 username=username,
316 email=email,
311 email=email,
317 password='trololo')
312 password='trololo')
318 response = api_call(self, params)
313 response = api_call(self, params)
319 expected = 'failed to create user `%s`' % username
314 expected = 'failed to create user `%s`' % username
320 self._compare_error(id_, expected, given=response.body)
315 self._compare_error(id_, expected, given=response.body)
321
316
322 def test_api_delete_user(self):
317 def test_api_delete_user(self):
323 usr = UserModel().create_or_update(username=u'test_user',
318 usr = UserModel().create_or_update(username=u'test_user',
324 password=u'qweqwe',
319 password=u'qweqwe',
325 email=u'u232@rhodecode.org',
320 email=u'u232@rhodecode.org',
326 firstname=u'u1', lastname=u'u1')
321 firstname=u'u1', lastname=u'u1')
327 Session().commit()
322 Session().commit()
328 username = usr.username
323 username = usr.username
329 email = usr.email
324 email = usr.email
330 usr_id = usr.user_id
325 usr_id = usr.user_id
331 ## DELETE THIS USER NOW
326 ## DELETE THIS USER NOW
332
327
333 id_, params = _build_data(self.apikey, 'delete_user',
328 id_, params = _build_data(self.apikey, 'delete_user',
334 userid=username,)
329 userid=username,)
335 response = api_call(self, params)
330 response = api_call(self, params)
336
331
337 ret = {'msg': 'deleted user ID:%s %s' % (usr_id, username),
332 ret = {'msg': 'deleted user ID:%s %s' % (usr_id, username),
338 'user': None}
333 'user': None}
339 expected = ret
334 expected = ret
340 self._compare_ok(id_, expected, given=response.body)
335 self._compare_ok(id_, expected, given=response.body)
341
336
342 @mock.patch.object(UserModel, 'delete', crash)
337 @mock.patch.object(UserModel, 'delete', crash)
343 def test_api_delete_user_when_exception_happened(self):
338 def test_api_delete_user_when_exception_happened(self):
344 usr = UserModel().create_or_update(username=u'test_user',
339 usr = UserModel().create_or_update(username=u'test_user',
345 password=u'qweqwe',
340 password=u'qweqwe',
346 email=u'u232@rhodecode.org',
341 email=u'u232@rhodecode.org',
347 firstname=u'u1', lastname=u'u1')
342 firstname=u'u1', lastname=u'u1')
348 Session().commit()
343 Session().commit()
349 username = usr.username
344 username = usr.username
350
345
351 id_, params = _build_data(self.apikey, 'delete_user',
346 id_, params = _build_data(self.apikey, 'delete_user',
352 userid=username,)
347 userid=username,)
353 response = api_call(self, params)
348 response = api_call(self, params)
354 ret = 'failed to delete ID:%s %s' % (usr.user_id,
349 ret = 'failed to delete ID:%s %s' % (usr.user_id,
355 usr.username)
350 usr.username)
356 expected = ret
351 expected = ret
357 self._compare_error(id_, expected, given=response.body)
352 self._compare_error(id_, expected, given=response.body)
358
353
359 @parameterized.expand([('firstname', 'new_username'),
354 @parameterized.expand([('firstname', 'new_username'),
360 ('lastname', 'new_username'),
355 ('lastname', 'new_username'),
361 ('email', 'new_username'),
356 ('email', 'new_username'),
362 ('admin', True),
357 ('admin', True),
363 ('admin', False),
358 ('admin', False),
364 ('ldap_dn', 'test'),
359 ('ldap_dn', 'test'),
365 ('ldap_dn', None),
360 ('ldap_dn', None),
366 ('active', False),
361 ('active', False),
367 ('active', True),
362 ('active', True),
368 ('password', 'newpass')
363 ('password', 'newpass')
369 ])
364 ])
370 def test_api_update_user(self, name, expected):
365 def test_api_update_user(self, name, expected):
371 usr = UserModel().get_by_username(self.TEST_USER_LOGIN)
366 usr = UserModel().get_by_username(self.TEST_USER_LOGIN)
372 kw = {name: expected,
367 kw = {name: expected,
373 'userid': usr.user_id}
368 'userid': usr.user_id}
374 id_, params = _build_data(self.apikey, 'update_user', **kw)
369 id_, params = _build_data(self.apikey, 'update_user', **kw)
375 response = api_call(self, params)
370 response = api_call(self, params)
376
371
377 ret = {
372 ret = {
378 'msg': 'updated user ID:%s %s' % (usr.user_id, self.TEST_USER_LOGIN),
373 'msg': 'updated user ID:%s %s' % (usr.user_id, self.TEST_USER_LOGIN),
379 'user': jsonify(UserModel()\
374 'user': jsonify(UserModel()\
380 .get_by_username(self.TEST_USER_LOGIN)\
375 .get_by_username(self.TEST_USER_LOGIN)\
381 .get_api_data())
376 .get_api_data())
382 }
377 }
383
378
384 expected = ret
379 expected = ret
385 self._compare_ok(id_, expected, given=response.body)
380 self._compare_ok(id_, expected, given=response.body)
386
381
387 def test_api_update_user_no_changed_params(self):
382 def test_api_update_user_no_changed_params(self):
388 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
383 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
389 ret = jsonify(usr.get_api_data())
384 ret = jsonify(usr.get_api_data())
390 id_, params = _build_data(self.apikey, 'update_user',
385 id_, params = _build_data(self.apikey, 'update_user',
391 userid=TEST_USER_ADMIN_LOGIN)
386 userid=TEST_USER_ADMIN_LOGIN)
392
387
393 response = api_call(self, params)
388 response = api_call(self, params)
394 ret = {
389 ret = {
395 'msg': 'updated user ID:%s %s' % (usr.user_id, TEST_USER_ADMIN_LOGIN),
390 'msg': 'updated user ID:%s %s' % (usr.user_id, TEST_USER_ADMIN_LOGIN),
396 'user': ret
391 'user': ret
397 }
392 }
398 expected = ret
393 expected = ret
399 self._compare_ok(id_, expected, given=response.body)
394 self._compare_ok(id_, expected, given=response.body)
400
395
401 def test_api_update_user_by_user_id(self):
396 def test_api_update_user_by_user_id(self):
402 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
397 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
403 ret = jsonify(usr.get_api_data())
398 ret = jsonify(usr.get_api_data())
404 id_, params = _build_data(self.apikey, 'update_user',
399 id_, params = _build_data(self.apikey, 'update_user',
405 userid=usr.user_id)
400 userid=usr.user_id)
406
401
407 response = api_call(self, params)
402 response = api_call(self, params)
408 ret = {
403 ret = {
409 'msg': 'updated user ID:%s %s' % (usr.user_id, TEST_USER_ADMIN_LOGIN),
404 'msg': 'updated user ID:%s %s' % (usr.user_id, TEST_USER_ADMIN_LOGIN),
410 'user': ret
405 'user': ret
411 }
406 }
412 expected = ret
407 expected = ret
413 self._compare_ok(id_, expected, given=response.body)
408 self._compare_ok(id_, expected, given=response.body)
414
409
415 @mock.patch.object(UserModel, 'update_user', crash)
410 @mock.patch.object(UserModel, 'update_user', crash)
416 def test_api_update_user_when_exception_happens(self):
411 def test_api_update_user_when_exception_happens(self):
417 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
412 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
418 ret = jsonify(usr.get_api_data())
413 ret = jsonify(usr.get_api_data())
419 id_, params = _build_data(self.apikey, 'update_user',
414 id_, params = _build_data(self.apikey, 'update_user',
420 userid=usr.user_id)
415 userid=usr.user_id)
421
416
422 response = api_call(self, params)
417 response = api_call(self, params)
423 ret = 'failed to update user `%s`' % usr.user_id
418 ret = 'failed to update user `%s`' % usr.user_id
424
419
425 expected = ret
420 expected = ret
426 self._compare_error(id_, expected, given=response.body)
421 self._compare_error(id_, expected, given=response.body)
427
422
428 def test_api_get_repo(self):
423 def test_api_get_repo(self):
429 new_group = 'some_new_group'
424 new_group = 'some_new_group'
430 make_users_group(new_group)
425 make_users_group(new_group)
431 RepoModel().grant_users_group_permission(repo=self.REPO,
426 RepoModel().grant_users_group_permission(repo=self.REPO,
432 group_name=new_group,
427 group_name=new_group,
433 perm='repository.read')
428 perm='repository.read')
434 Session().commit()
429 Session().commit()
435 id_, params = _build_data(self.apikey, 'get_repo',
430 id_, params = _build_data(self.apikey, 'get_repo',
436 repoid=self.REPO)
431 repoid=self.REPO)
437 response = api_call(self, params)
432 response = api_call(self, params)
438
433
439 repo = RepoModel().get_by_repo_name(self.REPO)
434 repo = RepoModel().get_by_repo_name(self.REPO)
440 ret = repo.get_api_data()
435 ret = repo.get_api_data()
441
436
442 members = []
437 members = []
443 for user in repo.repo_to_perm:
438 for user in repo.repo_to_perm:
444 perm = user.permission.permission_name
439 perm = user.permission.permission_name
445 user = user.user
440 user = user.user
446 user_data = user.get_api_data()
441 user_data = user.get_api_data()
447 user_data['type'] = "user"
442 user_data['type'] = "user"
448 user_data['permission'] = perm
443 user_data['permission'] = perm
449 members.append(user_data)
444 members.append(user_data)
450
445
451 for users_group in repo.users_group_to_perm:
446 for users_group in repo.users_group_to_perm:
452 perm = users_group.permission.permission_name
447 perm = users_group.permission.permission_name
453 users_group = users_group.users_group
448 users_group = users_group.users_group
454 users_group_data = users_group.get_api_data()
449 users_group_data = users_group.get_api_data()
455 users_group_data['type'] = "users_group"
450 users_group_data['type'] = "users_group"
456 users_group_data['permission'] = perm
451 users_group_data['permission'] = perm
457 members.append(users_group_data)
452 members.append(users_group_data)
458
453
459 ret['members'] = members
454 ret['members'] = members
460
455
461 expected = ret
456 expected = ret
462 self._compare_ok(id_, expected, given=response.body)
457 self._compare_ok(id_, expected, given=response.body)
463 destroy_users_group(new_group)
458 destroy_users_group(new_group)
464
459
465 def test_api_get_repo_that_doesn_not_exist(self):
460 def test_api_get_repo_that_doesn_not_exist(self):
466 id_, params = _build_data(self.apikey, 'get_repo',
461 id_, params = _build_data(self.apikey, 'get_repo',
467 repoid='no-such-repo')
462 repoid='no-such-repo')
468 response = api_call(self, params)
463 response = api_call(self, params)
469
464
470 ret = 'repository `%s` does not exist' % 'no-such-repo'
465 ret = 'repository `%s` does not exist' % 'no-such-repo'
471 expected = ret
466 expected = ret
472 self._compare_error(id_, expected, given=response.body)
467 self._compare_error(id_, expected, given=response.body)
473
468
474 def test_api_get_repos(self):
469 def test_api_get_repos(self):
475 id_, params = _build_data(self.apikey, 'get_repos')
470 id_, params = _build_data(self.apikey, 'get_repos')
476 response = api_call(self, params)
471 response = api_call(self, params)
477
472
478 result = []
473 result = []
479 for repo in RepoModel().get_all():
474 for repo in RepoModel().get_all():
480 result.append(repo.get_api_data())
475 result.append(repo.get_api_data())
481 ret = jsonify(result)
476 ret = jsonify(result)
482
477
483 expected = ret
478 expected = ret
484 self._compare_ok(id_, expected, given=response.body)
479 self._compare_ok(id_, expected, given=response.body)
485
480
486 @parameterized.expand([('all', 'all'),
481 @parameterized.expand([('all', 'all'),
487 ('dirs', 'dirs'),
482 ('dirs', 'dirs'),
488 ('files', 'files'), ])
483 ('files', 'files'), ])
489 def test_api_get_repo_nodes(self, name, ret_type):
484 def test_api_get_repo_nodes(self, name, ret_type):
490 rev = 'tip'
485 rev = 'tip'
491 path = '/'
486 path = '/'
492 id_, params = _build_data(self.apikey, 'get_repo_nodes',
487 id_, params = _build_data(self.apikey, 'get_repo_nodes',
493 repoid=self.REPO, revision=rev,
488 repoid=self.REPO, revision=rev,
494 root_path=path,
489 root_path=path,
495 ret_type=ret_type)
490 ret_type=ret_type)
496 response = api_call(self, params)
491 response = api_call(self, params)
497
492
498 # we don't the actual return types here since it's tested somewhere
493 # we don't the actual return types here since it's tested somewhere
499 # else
494 # else
500 expected = json.loads(response.body)['result']
495 expected = json.loads(response.body)['result']
501 self._compare_ok(id_, expected, given=response.body)
496 self._compare_ok(id_, expected, given=response.body)
502
497
503 def test_api_get_repo_nodes_bad_revisions(self):
498 def test_api_get_repo_nodes_bad_revisions(self):
504 rev = 'i-dont-exist'
499 rev = 'i-dont-exist'
505 path = '/'
500 path = '/'
506 id_, params = _build_data(self.apikey, 'get_repo_nodes',
501 id_, params = _build_data(self.apikey, 'get_repo_nodes',
507 repoid=self.REPO, revision=rev,
502 repoid=self.REPO, revision=rev,
508 root_path=path,)
503 root_path=path,)
509 response = api_call(self, params)
504 response = api_call(self, params)
510
505
511 expected = 'failed to get repo: `%s` nodes' % self.REPO
506 expected = 'failed to get repo: `%s` nodes' % self.REPO
512 self._compare_error(id_, expected, given=response.body)
507 self._compare_error(id_, expected, given=response.body)
513
508
514 def test_api_get_repo_nodes_bad_path(self):
509 def test_api_get_repo_nodes_bad_path(self):
515 rev = 'tip'
510 rev = 'tip'
516 path = '/idontexits'
511 path = '/idontexits'
517 id_, params = _build_data(self.apikey, 'get_repo_nodes',
512 id_, params = _build_data(self.apikey, 'get_repo_nodes',
518 repoid=self.REPO, revision=rev,
513 repoid=self.REPO, revision=rev,
519 root_path=path,)
514 root_path=path,)
520 response = api_call(self, params)
515 response = api_call(self, params)
521
516
522 expected = 'failed to get repo: `%s` nodes' % self.REPO
517 expected = 'failed to get repo: `%s` nodes' % self.REPO
523 self._compare_error(id_, expected, given=response.body)
518 self._compare_error(id_, expected, given=response.body)
524
519
525 def test_api_get_repo_nodes_bad_ret_type(self):
520 def test_api_get_repo_nodes_bad_ret_type(self):
526 rev = 'tip'
521 rev = 'tip'
527 path = '/'
522 path = '/'
528 ret_type = 'error'
523 ret_type = 'error'
529 id_, params = _build_data(self.apikey, 'get_repo_nodes',
524 id_, params = _build_data(self.apikey, 'get_repo_nodes',
530 repoid=self.REPO, revision=rev,
525 repoid=self.REPO, revision=rev,
531 root_path=path,
526 root_path=path,
532 ret_type=ret_type)
527 ret_type=ret_type)
533 response = api_call(self, params)
528 response = api_call(self, params)
534
529
535 expected = 'ret_type must be one of %s' % (['files', 'dirs', 'all'])
530 expected = 'ret_type must be one of %s' % (['files', 'dirs', 'all'])
536 self._compare_error(id_, expected, given=response.body)
531 self._compare_error(id_, expected, given=response.body)
537
532
538 def test_api_create_repo(self):
533 def test_api_create_repo(self):
539 repo_name = 'api-repo'
534 repo_name = 'api-repo'
540 id_, params = _build_data(self.apikey, 'create_repo',
535 id_, params = _build_data(self.apikey, 'create_repo',
541 repo_name=repo_name,
536 repo_name=repo_name,
542 owner=TEST_USER_ADMIN_LOGIN,
537 owner=TEST_USER_ADMIN_LOGIN,
543 repo_type='hg',
538 repo_type='hg',
544 )
539 )
545 response = api_call(self, params)
540 response = api_call(self, params)
546
541
547 repo = RepoModel().get_by_repo_name(repo_name)
542 repo = RepoModel().get_by_repo_name(repo_name)
548 ret = {
543 ret = {
549 'msg': 'Created new repository `%s`' % repo_name,
544 'msg': 'Created new repository `%s`' % repo_name,
550 'repo': jsonify(repo.get_api_data())
545 'repo': jsonify(repo.get_api_data())
551 }
546 }
552 expected = ret
547 expected = ret
553 self._compare_ok(id_, expected, given=response.body)
548 self._compare_ok(id_, expected, given=response.body)
554 destroy_repo(repo_name)
549 destroy_repo(repo_name)
555
550
556 def test_api_create_repo_unknown_owner(self):
551 def test_api_create_repo_unknown_owner(self):
557 repo_name = 'api-repo'
552 repo_name = 'api-repo'
558 owner = 'i-dont-exist'
553 owner = 'i-dont-exist'
559 id_, params = _build_data(self.apikey, 'create_repo',
554 id_, params = _build_data(self.apikey, 'create_repo',
560 repo_name=repo_name,
555 repo_name=repo_name,
561 owner=owner,
556 owner=owner,
562 repo_type='hg',
557 repo_type='hg',
563 )
558 )
564 response = api_call(self, params)
559 response = api_call(self, params)
565 expected = 'user `%s` does not exist' % owner
560 expected = 'user `%s` does not exist' % owner
566 self._compare_error(id_, expected, given=response.body)
561 self._compare_error(id_, expected, given=response.body)
567
562
568 def test_api_create_repo_exists(self):
563 def test_api_create_repo_exists(self):
569 repo_name = self.REPO
564 repo_name = self.REPO
570 id_, params = _build_data(self.apikey, 'create_repo',
565 id_, params = _build_data(self.apikey, 'create_repo',
571 repo_name=repo_name,
566 repo_name=repo_name,
572 owner=TEST_USER_ADMIN_LOGIN,
567 owner=TEST_USER_ADMIN_LOGIN,
573 repo_type='hg',
568 repo_type='hg',
574 )
569 )
575 response = api_call(self, params)
570 response = api_call(self, params)
576 expected = "repo `%s` already exist" % repo_name
571 expected = "repo `%s` already exist" % repo_name
577 self._compare_error(id_, expected, given=response.body)
572 self._compare_error(id_, expected, given=response.body)
578
573
579 @mock.patch.object(RepoModel, 'create_repo', crash)
574 @mock.patch.object(RepoModel, 'create_repo', crash)
580 def test_api_create_repo_exception_occurred(self):
575 def test_api_create_repo_exception_occurred(self):
581 repo_name = 'api-repo'
576 repo_name = 'api-repo'
582 id_, params = _build_data(self.apikey, 'create_repo',
577 id_, params = _build_data(self.apikey, 'create_repo',
583 repo_name=repo_name,
578 repo_name=repo_name,
584 owner=TEST_USER_ADMIN_LOGIN,
579 owner=TEST_USER_ADMIN_LOGIN,
585 repo_type='hg',
580 repo_type='hg',
586 )
581 )
587 response = api_call(self, params)
582 response = api_call(self, params)
588 expected = 'failed to create repository `%s`' % repo_name
583 expected = 'failed to create repository `%s`' % repo_name
589 self._compare_error(id_, expected, given=response.body)
584 self._compare_error(id_, expected, given=response.body)
590
585
591 def test_api_delete_repo(self):
586 def test_api_delete_repo(self):
592 repo_name = 'api_delete_me'
587 repo_name = 'api_delete_me'
593 create_repo(repo_name, self.REPO_TYPE)
588 create_repo(repo_name, self.REPO_TYPE)
594
589
595 id_, params = _build_data(self.apikey, 'delete_repo',
590 id_, params = _build_data(self.apikey, 'delete_repo',
596 repoid=repo_name,)
591 repoid=repo_name,)
597 response = api_call(self, params)
592 response = api_call(self, params)
598
593
599 ret = {
594 ret = {
600 'msg': 'Deleted repository `%s`' % repo_name,
595 'msg': 'Deleted repository `%s`' % repo_name,
601 'success': True
596 'success': True
602 }
597 }
603 expected = ret
598 expected = ret
604 self._compare_ok(id_, expected, given=response.body)
599 self._compare_ok(id_, expected, given=response.body)
605
600
606 def test_api_delete_repo_exception_occurred(self):
601 def test_api_delete_repo_exception_occurred(self):
607 repo_name = 'api_delete_me'
602 repo_name = 'api_delete_me'
608 create_repo(repo_name, self.REPO_TYPE)
603 create_repo(repo_name, self.REPO_TYPE)
609 try:
604 try:
610 with mock.patch.object(RepoModel, 'delete', crash):
605 with mock.patch.object(RepoModel, 'delete', crash):
611 id_, params = _build_data(self.apikey, 'delete_repo',
606 id_, params = _build_data(self.apikey, 'delete_repo',
612 repoid=repo_name,)
607 repoid=repo_name,)
613 response = api_call(self, params)
608 response = api_call(self, params)
614
609
615 expected = 'failed to delete repository `%s`' % repo_name
610 expected = 'failed to delete repository `%s`' % repo_name
616 self._compare_error(id_, expected, given=response.body)
611 self._compare_error(id_, expected, given=response.body)
617 finally:
612 finally:
618 destroy_repo(repo_name)
613 destroy_repo(repo_name)
619
614
620 def test_api_fork_repo(self):
615 def test_api_fork_repo(self):
621 fork_name = 'api-repo-fork'
616 fork_name = 'api-repo-fork'
622 id_, params = _build_data(self.apikey, 'fork_repo',
617 id_, params = _build_data(self.apikey, 'fork_repo',
623 repoid=self.REPO,
618 repoid=self.REPO,
624 fork_name=fork_name,
619 fork_name=fork_name,
625 owner=TEST_USER_ADMIN_LOGIN,
620 owner=TEST_USER_ADMIN_LOGIN,
626 )
621 )
627 response = api_call(self, params)
622 response = api_call(self, params)
628
623
629 ret = {
624 ret = {
630 'msg': 'Created fork of `%s` as `%s`' % (self.REPO,
625 'msg': 'Created fork of `%s` as `%s`' % (self.REPO,
631 fork_name),
626 fork_name),
632 'success': True
627 'success': True
633 }
628 }
634 expected = ret
629 expected = ret
635 self._compare_ok(id_, expected, given=response.body)
630 self._compare_ok(id_, expected, given=response.body)
636 destroy_repo(fork_name)
631 destroy_repo(fork_name)
637
632
638 def test_api_fork_repo_unknown_owner(self):
633 def test_api_fork_repo_unknown_owner(self):
639 fork_name = 'api-repo-fork'
634 fork_name = 'api-repo-fork'
640 owner = 'i-dont-exist'
635 owner = 'i-dont-exist'
641 id_, params = _build_data(self.apikey, 'fork_repo',
636 id_, params = _build_data(self.apikey, 'fork_repo',
642 repoid=self.REPO,
637 repoid=self.REPO,
643 fork_name=fork_name,
638 fork_name=fork_name,
644 owner=owner,
639 owner=owner,
645 )
640 )
646 response = api_call(self, params)
641 response = api_call(self, params)
647 expected = 'user `%s` does not exist' % owner
642 expected = 'user `%s` does not exist' % owner
648 self._compare_error(id_, expected, given=response.body)
643 self._compare_error(id_, expected, given=response.body)
649
644
650 def test_api_fork_repo_fork_exists(self):
645 def test_api_fork_repo_fork_exists(self):
651 fork_name = 'api-repo-fork'
646 fork_name = 'api-repo-fork'
652 create_fork(fork_name, self.REPO_TYPE, self.REPO)
647 create_fork(fork_name, self.REPO_TYPE, self.REPO)
653
648
654 try:
649 try:
655 fork_name = 'api-repo-fork'
650 fork_name = 'api-repo-fork'
656
651
657 id_, params = _build_data(self.apikey, 'fork_repo',
652 id_, params = _build_data(self.apikey, 'fork_repo',
658 repoid=self.REPO,
653 repoid=self.REPO,
659 fork_name=fork_name,
654 fork_name=fork_name,
660 owner=TEST_USER_ADMIN_LOGIN,
655 owner=TEST_USER_ADMIN_LOGIN,
661 )
656 )
662 response = api_call(self, params)
657 response = api_call(self, params)
663
658
664 expected = "fork `%s` already exist" % fork_name
659 expected = "fork `%s` already exist" % fork_name
665 self._compare_error(id_, expected, given=response.body)
660 self._compare_error(id_, expected, given=response.body)
666 finally:
661 finally:
667 destroy_repo(fork_name)
662 destroy_repo(fork_name)
668
663
669 def test_api_fork_repo_repo_exists(self):
664 def test_api_fork_repo_repo_exists(self):
670 fork_name = self.REPO
665 fork_name = self.REPO
671
666
672 id_, params = _build_data(self.apikey, 'fork_repo',
667 id_, params = _build_data(self.apikey, 'fork_repo',
673 repoid=self.REPO,
668 repoid=self.REPO,
674 fork_name=fork_name,
669 fork_name=fork_name,
675 owner=TEST_USER_ADMIN_LOGIN,
670 owner=TEST_USER_ADMIN_LOGIN,
676 )
671 )
677 response = api_call(self, params)
672 response = api_call(self, params)
678
673
679 expected = "repo `%s` already exist" % fork_name
674 expected = "repo `%s` already exist" % fork_name
680 self._compare_error(id_, expected, given=response.body)
675 self._compare_error(id_, expected, given=response.body)
681
676
682 @mock.patch.object(RepoModel, 'create_fork', crash)
677 @mock.patch.object(RepoModel, 'create_fork', crash)
683 def test_api_fork_repo_exception_occurred(self):
678 def test_api_fork_repo_exception_occurred(self):
684 fork_name = 'api-repo-fork'
679 fork_name = 'api-repo-fork'
685 id_, params = _build_data(self.apikey, 'fork_repo',
680 id_, params = _build_data(self.apikey, 'fork_repo',
686 repoid=self.REPO,
681 repoid=self.REPO,
687 fork_name=fork_name,
682 fork_name=fork_name,
688 owner=TEST_USER_ADMIN_LOGIN,
683 owner=TEST_USER_ADMIN_LOGIN,
689 )
684 )
690 response = api_call(self, params)
685 response = api_call(self, params)
691
686
692 expected = 'failed to fork repository `%s` as `%s`' % (self.REPO,
687 expected = 'failed to fork repository `%s` as `%s`' % (self.REPO,
693 fork_name)
688 fork_name)
694 self._compare_error(id_, expected, given=response.body)
689 self._compare_error(id_, expected, given=response.body)
695
690
696 def test_api_get_users_group(self):
691 def test_api_get_users_group(self):
697 id_, params = _build_data(self.apikey, 'get_users_group',
692 id_, params = _build_data(self.apikey, 'get_users_group',
698 usersgroupid=TEST_USERS_GROUP)
693 usersgroupid=TEST_USERS_GROUP)
699 response = api_call(self, params)
694 response = api_call(self, params)
700
695
701 users_group = UsersGroupModel().get_group(TEST_USERS_GROUP)
696 users_group = UsersGroupModel().get_group(TEST_USERS_GROUP)
702 members = []
697 members = []
703 for user in users_group.members:
698 for user in users_group.members:
704 user = user.user
699 user = user.user
705 members.append(user.get_api_data())
700 members.append(user.get_api_data())
706
701
707 ret = users_group.get_api_data()
702 ret = users_group.get_api_data()
708 ret['members'] = members
703 ret['members'] = members
709 expected = ret
704 expected = ret
710 self._compare_ok(id_, expected, given=response.body)
705 self._compare_ok(id_, expected, given=response.body)
711
706
712 def test_api_get_users_groups(self):
707 def test_api_get_users_groups(self):
713
708
714 make_users_group('test_users_group2')
709 make_users_group('test_users_group2')
715
710
716 id_, params = _build_data(self.apikey, 'get_users_groups',)
711 id_, params = _build_data(self.apikey, 'get_users_groups',)
717 response = api_call(self, params)
712 response = api_call(self, params)
718
713
719 expected = []
714 expected = []
720 for gr_name in [TEST_USERS_GROUP, 'test_users_group2']:
715 for gr_name in [TEST_USERS_GROUP, 'test_users_group2']:
721 users_group = UsersGroupModel().get_group(gr_name)
716 users_group = UsersGroupModel().get_group(gr_name)
722 ret = users_group.get_api_data()
717 ret = users_group.get_api_data()
723 expected.append(ret)
718 expected.append(ret)
724 self._compare_ok(id_, expected, given=response.body)
719 self._compare_ok(id_, expected, given=response.body)
725
720
726 UsersGroupModel().delete(users_group='test_users_group2')
721 UsersGroupModel().delete(users_group='test_users_group2')
727 Session().commit()
722 Session().commit()
728
723
729 def test_api_create_users_group(self):
724 def test_api_create_users_group(self):
730 group_name = 'some_new_group'
725 group_name = 'some_new_group'
731 id_, params = _build_data(self.apikey, 'create_users_group',
726 id_, params = _build_data(self.apikey, 'create_users_group',
732 group_name=group_name)
727 group_name=group_name)
733 response = api_call(self, params)
728 response = api_call(self, params)
734
729
735 ret = {
730 ret = {
736 'msg': 'created new users group `%s`' % group_name,
731 'msg': 'created new users group `%s`' % group_name,
737 'users_group': jsonify(UsersGroupModel()\
732 'users_group': jsonify(UsersGroupModel()\
738 .get_by_name(group_name)\
733 .get_by_name(group_name)\
739 .get_api_data())
734 .get_api_data())
740 }
735 }
741 expected = ret
736 expected = ret
742 self._compare_ok(id_, expected, given=response.body)
737 self._compare_ok(id_, expected, given=response.body)
743
738
744 destroy_users_group(group_name)
739 destroy_users_group(group_name)
745
740
746 def test_api_get_users_group_that_exist(self):
741 def test_api_get_users_group_that_exist(self):
747 id_, params = _build_data(self.apikey, 'create_users_group',
742 id_, params = _build_data(self.apikey, 'create_users_group',
748 group_name=TEST_USERS_GROUP)
743 group_name=TEST_USERS_GROUP)
749 response = api_call(self, params)
744 response = api_call(self, params)
750
745
751 expected = "users group `%s` already exist" % TEST_USERS_GROUP
746 expected = "users group `%s` already exist" % TEST_USERS_GROUP
752 self._compare_error(id_, expected, given=response.body)
747 self._compare_error(id_, expected, given=response.body)
753
748
754 @mock.patch.object(UsersGroupModel, 'create', crash)
749 @mock.patch.object(UsersGroupModel, 'create', crash)
755 def test_api_get_users_group_exception_occurred(self):
750 def test_api_get_users_group_exception_occurred(self):
756 group_name = 'exception_happens'
751 group_name = 'exception_happens'
757 id_, params = _build_data(self.apikey, 'create_users_group',
752 id_, params = _build_data(self.apikey, 'create_users_group',
758 group_name=group_name)
753 group_name=group_name)
759 response = api_call(self, params)
754 response = api_call(self, params)
760
755
761 expected = 'failed to create group `%s`' % group_name
756 expected = 'failed to create group `%s`' % group_name
762 self._compare_error(id_, expected, given=response.body)
757 self._compare_error(id_, expected, given=response.body)
763
758
764 def test_api_add_user_to_users_group(self):
759 def test_api_add_user_to_users_group(self):
765 gr_name = 'test_group'
760 gr_name = 'test_group'
766 UsersGroupModel().create(gr_name)
761 UsersGroupModel().create(gr_name)
767 Session().commit()
762 Session().commit()
768 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
763 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
769 usersgroupid=gr_name,
764 usersgroupid=gr_name,
770 userid=TEST_USER_ADMIN_LOGIN)
765 userid=TEST_USER_ADMIN_LOGIN)
771 response = api_call(self, params)
766 response = api_call(self, params)
772
767
773 expected = {
768 expected = {
774 'msg': 'added member `%s` to users group `%s`' % (
769 'msg': 'added member `%s` to users group `%s`' % (
775 TEST_USER_ADMIN_LOGIN, gr_name
770 TEST_USER_ADMIN_LOGIN, gr_name
776 ),
771 ),
777 'success': True}
772 'success': True}
778 self._compare_ok(id_, expected, given=response.body)
773 self._compare_ok(id_, expected, given=response.body)
779
774
780 UsersGroupModel().delete(users_group=gr_name)
775 UsersGroupModel().delete(users_group=gr_name)
781 Session().commit()
776 Session().commit()
782
777
783 def test_api_add_user_to_users_group_that_doesnt_exist(self):
778 def test_api_add_user_to_users_group_that_doesnt_exist(self):
784 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
779 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
785 usersgroupid='false-group',
780 usersgroupid='false-group',
786 userid=TEST_USER_ADMIN_LOGIN)
781 userid=TEST_USER_ADMIN_LOGIN)
787 response = api_call(self, params)
782 response = api_call(self, params)
788
783
789 expected = 'users group `%s` does not exist' % 'false-group'
784 expected = 'users group `%s` does not exist' % 'false-group'
790 self._compare_error(id_, expected, given=response.body)
785 self._compare_error(id_, expected, given=response.body)
791
786
792 @mock.patch.object(UsersGroupModel, 'add_user_to_group', crash)
787 @mock.patch.object(UsersGroupModel, 'add_user_to_group', crash)
793 def test_api_add_user_to_users_group_exception_occurred(self):
788 def test_api_add_user_to_users_group_exception_occurred(self):
794 gr_name = 'test_group'
789 gr_name = 'test_group'
795 UsersGroupModel().create(gr_name)
790 UsersGroupModel().create(gr_name)
796 Session().commit()
791 Session().commit()
797 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
792 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
798 usersgroupid=gr_name,
793 usersgroupid=gr_name,
799 userid=TEST_USER_ADMIN_LOGIN)
794 userid=TEST_USER_ADMIN_LOGIN)
800 response = api_call(self, params)
795 response = api_call(self, params)
801
796
802 expected = 'failed to add member to users group `%s`' % gr_name
797 expected = 'failed to add member to users group `%s`' % gr_name
803 self._compare_error(id_, expected, given=response.body)
798 self._compare_error(id_, expected, given=response.body)
804
799
805 UsersGroupModel().delete(users_group=gr_name)
800 UsersGroupModel().delete(users_group=gr_name)
806 Session().commit()
801 Session().commit()
807
802
808 def test_api_remove_user_from_users_group(self):
803 def test_api_remove_user_from_users_group(self):
809 gr_name = 'test_group_3'
804 gr_name = 'test_group_3'
810 gr = UsersGroupModel().create(gr_name)
805 gr = UsersGroupModel().create(gr_name)
811 UsersGroupModel().add_user_to_group(gr, user=TEST_USER_ADMIN_LOGIN)
806 UsersGroupModel().add_user_to_group(gr, user=TEST_USER_ADMIN_LOGIN)
812 Session().commit()
807 Session().commit()
813 id_, params = _build_data(self.apikey, 'remove_user_from_users_group',
808 id_, params = _build_data(self.apikey, 'remove_user_from_users_group',
814 usersgroupid=gr_name,
809 usersgroupid=gr_name,
815 userid=TEST_USER_ADMIN_LOGIN)
810 userid=TEST_USER_ADMIN_LOGIN)
816 response = api_call(self, params)
811 response = api_call(self, params)
817
812
818 expected = {
813 expected = {
819 'msg': 'removed member `%s` from users group `%s`' % (
814 'msg': 'removed member `%s` from users group `%s`' % (
820 TEST_USER_ADMIN_LOGIN, gr_name
815 TEST_USER_ADMIN_LOGIN, gr_name
821 ),
816 ),
822 'success': True}
817 'success': True}
823 self._compare_ok(id_, expected, given=response.body)
818 self._compare_ok(id_, expected, given=response.body)
824
819
825 UsersGroupModel().delete(users_group=gr_name)
820 UsersGroupModel().delete(users_group=gr_name)
826 Session().commit()
821 Session().commit()
827
822
828 @mock.patch.object(UsersGroupModel, 'remove_user_from_group', crash)
823 @mock.patch.object(UsersGroupModel, 'remove_user_from_group', crash)
829 def test_api_remove_user_from_users_group_exception_occurred(self):
824 def test_api_remove_user_from_users_group_exception_occurred(self):
830 gr_name = 'test_group_3'
825 gr_name = 'test_group_3'
831 gr = UsersGroupModel().create(gr_name)
826 gr = UsersGroupModel().create(gr_name)
832 UsersGroupModel().add_user_to_group(gr, user=TEST_USER_ADMIN_LOGIN)
827 UsersGroupModel().add_user_to_group(gr, user=TEST_USER_ADMIN_LOGIN)
833 Session().commit()
828 Session().commit()
834 id_, params = _build_data(self.apikey, 'remove_user_from_users_group',
829 id_, params = _build_data(self.apikey, 'remove_user_from_users_group',
835 usersgroupid=gr_name,
830 usersgroupid=gr_name,
836 userid=TEST_USER_ADMIN_LOGIN)
831 userid=TEST_USER_ADMIN_LOGIN)
837 response = api_call(self, params)
832 response = api_call(self, params)
838
833
839 expected = 'failed to remove member from users group `%s`' % gr_name
834 expected = 'failed to remove member from users group `%s`' % gr_name
840 self._compare_error(id_, expected, given=response.body)
835 self._compare_error(id_, expected, given=response.body)
841
836
842 UsersGroupModel().delete(users_group=gr_name)
837 UsersGroupModel().delete(users_group=gr_name)
843 Session().commit()
838 Session().commit()
844
839
845 @parameterized.expand([('none', 'repository.none'),
840 @parameterized.expand([('none', 'repository.none'),
846 ('read', 'repository.read'),
841 ('read', 'repository.read'),
847 ('write', 'repository.write'),
842 ('write', 'repository.write'),
848 ('admin', 'repository.admin')])
843 ('admin', 'repository.admin')])
849 def test_api_grant_user_permission(self, name, perm):
844 def test_api_grant_user_permission(self, name, perm):
850 id_, params = _build_data(self.apikey, 'grant_user_permission',
845 id_, params = _build_data(self.apikey, 'grant_user_permission',
851 repoid=self.REPO,
846 repoid=self.REPO,
852 userid=TEST_USER_ADMIN_LOGIN,
847 userid=TEST_USER_ADMIN_LOGIN,
853 perm=perm)
848 perm=perm)
854 response = api_call(self, params)
849 response = api_call(self, params)
855
850
856 ret = {
851 ret = {
857 'msg': 'Granted perm: `%s` for user: `%s` in repo: `%s`' % (
852 'msg': 'Granted perm: `%s` for user: `%s` in repo: `%s`' % (
858 perm, TEST_USER_ADMIN_LOGIN, self.REPO
853 perm, TEST_USER_ADMIN_LOGIN, self.REPO
859 ),
854 ),
860 'success': True
855 'success': True
861 }
856 }
862 expected = ret
857 expected = ret
863 self._compare_ok(id_, expected, given=response.body)
858 self._compare_ok(id_, expected, given=response.body)
864
859
865 def test_api_grant_user_permission_wrong_permission(self):
860 def test_api_grant_user_permission_wrong_permission(self):
866 perm = 'haha.no.permission'
861 perm = 'haha.no.permission'
867 id_, params = _build_data(self.apikey, 'grant_user_permission',
862 id_, params = _build_data(self.apikey, 'grant_user_permission',
868 repoid=self.REPO,
863 repoid=self.REPO,
869 userid=TEST_USER_ADMIN_LOGIN,
864 userid=TEST_USER_ADMIN_LOGIN,
870 perm=perm)
865 perm=perm)
871 response = api_call(self, params)
866 response = api_call(self, params)
872
867
873 expected = 'permission `%s` does not exist' % perm
868 expected = 'permission `%s` does not exist' % perm
874 self._compare_error(id_, expected, given=response.body)
869 self._compare_error(id_, expected, given=response.body)
875
870
876 @mock.patch.object(RepoModel, 'grant_user_permission', crash)
871 @mock.patch.object(RepoModel, 'grant_user_permission', crash)
877 def test_api_grant_user_permission_exception_when_adding(self):
872 def test_api_grant_user_permission_exception_when_adding(self):
878 perm = 'repository.read'
873 perm = 'repository.read'
879 id_, params = _build_data(self.apikey, 'grant_user_permission',
874 id_, params = _build_data(self.apikey, 'grant_user_permission',
880 repoid=self.REPO,
875 repoid=self.REPO,
881 userid=TEST_USER_ADMIN_LOGIN,
876 userid=TEST_USER_ADMIN_LOGIN,
882 perm=perm)
877 perm=perm)
883 response = api_call(self, params)
878 response = api_call(self, params)
884
879
885 expected = 'failed to edit permission for user: `%s` in repo: `%s`' % (
880 expected = 'failed to edit permission for user: `%s` in repo: `%s`' % (
886 TEST_USER_ADMIN_LOGIN, self.REPO
881 TEST_USER_ADMIN_LOGIN, self.REPO
887 )
882 )
888 self._compare_error(id_, expected, given=response.body)
883 self._compare_error(id_, expected, given=response.body)
889
884
890 def test_api_revoke_user_permission(self):
885 def test_api_revoke_user_permission(self):
891 id_, params = _build_data(self.apikey, 'revoke_user_permission',
886 id_, params = _build_data(self.apikey, 'revoke_user_permission',
892 repoid=self.REPO,
887 repoid=self.REPO,
893 userid=TEST_USER_ADMIN_LOGIN,)
888 userid=TEST_USER_ADMIN_LOGIN,)
894 response = api_call(self, params)
889 response = api_call(self, params)
895
890
896 expected = {
891 expected = {
897 'msg': 'Revoked perm for user: `%s` in repo: `%s`' % (
892 'msg': 'Revoked perm for user: `%s` in repo: `%s`' % (
898 TEST_USER_ADMIN_LOGIN, self.REPO
893 TEST_USER_ADMIN_LOGIN, self.REPO
899 ),
894 ),
900 'success': True
895 'success': True
901 }
896 }
902 self._compare_ok(id_, expected, given=response.body)
897 self._compare_ok(id_, expected, given=response.body)
903
898
904 @mock.patch.object(RepoModel, 'revoke_user_permission', crash)
899 @mock.patch.object(RepoModel, 'revoke_user_permission', crash)
905 def test_api_revoke_user_permission_exception_when_adding(self):
900 def test_api_revoke_user_permission_exception_when_adding(self):
906 id_, params = _build_data(self.apikey, 'revoke_user_permission',
901 id_, params = _build_data(self.apikey, 'revoke_user_permission',
907 repoid=self.REPO,
902 repoid=self.REPO,
908 userid=TEST_USER_ADMIN_LOGIN,)
903 userid=TEST_USER_ADMIN_LOGIN,)
909 response = api_call(self, params)
904 response = api_call(self, params)
910
905
911 expected = 'failed to edit permission for user: `%s` in repo: `%s`' % (
906 expected = 'failed to edit permission for user: `%s` in repo: `%s`' % (
912 TEST_USER_ADMIN_LOGIN, self.REPO
907 TEST_USER_ADMIN_LOGIN, self.REPO
913 )
908 )
914 self._compare_error(id_, expected, given=response.body)
909 self._compare_error(id_, expected, given=response.body)
915
910
916 @parameterized.expand([('none', 'repository.none'),
911 @parameterized.expand([('none', 'repository.none'),
917 ('read', 'repository.read'),
912 ('read', 'repository.read'),
918 ('write', 'repository.write'),
913 ('write', 'repository.write'),
919 ('admin', 'repository.admin')])
914 ('admin', 'repository.admin')])
920 def test_api_grant_users_group_permission(self, name, perm):
915 def test_api_grant_users_group_permission(self, name, perm):
921 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
916 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
922 repoid=self.REPO,
917 repoid=self.REPO,
923 usersgroupid=TEST_USERS_GROUP,
918 usersgroupid=TEST_USERS_GROUP,
924 perm=perm)
919 perm=perm)
925 response = api_call(self, params)
920 response = api_call(self, params)
926
921
927 ret = {
922 ret = {
928 'msg': 'Granted perm: `%s` for users group: `%s` in repo: `%s`' % (
923 'msg': 'Granted perm: `%s` for users group: `%s` in repo: `%s`' % (
929 perm, TEST_USERS_GROUP, self.REPO
924 perm, TEST_USERS_GROUP, self.REPO
930 ),
925 ),
931 'success': True
926 'success': True
932 }
927 }
933 expected = ret
928 expected = ret
934 self._compare_ok(id_, expected, given=response.body)
929 self._compare_ok(id_, expected, given=response.body)
935
930
936 def test_api_grant_users_group_permission_wrong_permission(self):
931 def test_api_grant_users_group_permission_wrong_permission(self):
937 perm = 'haha.no.permission'
932 perm = 'haha.no.permission'
938 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
933 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
939 repoid=self.REPO,
934 repoid=self.REPO,
940 usersgroupid=TEST_USERS_GROUP,
935 usersgroupid=TEST_USERS_GROUP,
941 perm=perm)
936 perm=perm)
942 response = api_call(self, params)
937 response = api_call(self, params)
943
938
944 expected = 'permission `%s` does not exist' % perm
939 expected = 'permission `%s` does not exist' % perm
945 self._compare_error(id_, expected, given=response.body)
940 self._compare_error(id_, expected, given=response.body)
946
941
947 @mock.patch.object(RepoModel, 'grant_users_group_permission', crash)
942 @mock.patch.object(RepoModel, 'grant_users_group_permission', crash)
948 def test_api_grant_users_group_permission_exception_when_adding(self):
943 def test_api_grant_users_group_permission_exception_when_adding(self):
949 perm = 'repository.read'
944 perm = 'repository.read'
950 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
945 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
951 repoid=self.REPO,
946 repoid=self.REPO,
952 usersgroupid=TEST_USERS_GROUP,
947 usersgroupid=TEST_USERS_GROUP,
953 perm=perm)
948 perm=perm)
954 response = api_call(self, params)
949 response = api_call(self, params)
955
950
956 expected = 'failed to edit permission for users group: `%s` in repo: `%s`' % (
951 expected = 'failed to edit permission for users group: `%s` in repo: `%s`' % (
957 TEST_USERS_GROUP, self.REPO
952 TEST_USERS_GROUP, self.REPO
958 )
953 )
959 self._compare_error(id_, expected, given=response.body)
954 self._compare_error(id_, expected, given=response.body)
960
955
961 def test_api_revoke_users_group_permission(self):
956 def test_api_revoke_users_group_permission(self):
962 RepoModel().grant_users_group_permission(repo=self.REPO,
957 RepoModel().grant_users_group_permission(repo=self.REPO,
963 group_name=TEST_USERS_GROUP,
958 group_name=TEST_USERS_GROUP,
964 perm='repository.read')
959 perm='repository.read')
965 Session().commit()
960 Session().commit()
966 id_, params = _build_data(self.apikey, 'revoke_users_group_permission',
961 id_, params = _build_data(self.apikey, 'revoke_users_group_permission',
967 repoid=self.REPO,
962 repoid=self.REPO,
968 usersgroupid=TEST_USERS_GROUP,)
963 usersgroupid=TEST_USERS_GROUP,)
969 response = api_call(self, params)
964 response = api_call(self, params)
970
965
971 expected = {
966 expected = {
972 'msg': 'Revoked perm for users group: `%s` in repo: `%s`' % (
967 'msg': 'Revoked perm for users group: `%s` in repo: `%s`' % (
973 TEST_USERS_GROUP, self.REPO
968 TEST_USERS_GROUP, self.REPO
974 ),
969 ),
975 'success': True
970 'success': True
976 }
971 }
977 self._compare_ok(id_, expected, given=response.body)
972 self._compare_ok(id_, expected, given=response.body)
978
973
979 @mock.patch.object(RepoModel, 'revoke_users_group_permission', crash)
974 @mock.patch.object(RepoModel, 'revoke_users_group_permission', crash)
980 def test_api_revoke_users_group_permission_exception_when_adding(self):
975 def test_api_revoke_users_group_permission_exception_when_adding(self):
981
976
982 id_, params = _build_data(self.apikey, 'revoke_users_group_permission',
977 id_, params = _build_data(self.apikey, 'revoke_users_group_permission',
983 repoid=self.REPO,
978 repoid=self.REPO,
984 usersgroupid=TEST_USERS_GROUP,)
979 usersgroupid=TEST_USERS_GROUP,)
985 response = api_call(self, params)
980 response = api_call(self, params)
986
981
987 expected = 'failed to edit permission for users group: `%s` in repo: `%s`' % (
982 expected = 'failed to edit permission for users group: `%s` in repo: `%s`' % (
988 TEST_USERS_GROUP, self.REPO
983 TEST_USERS_GROUP, self.REPO
989 )
984 )
990 self._compare_error(id_, expected, given=response.body)
985 self._compare_error(id_, expected, given=response.body)
@@ -1,340 +1,326 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 import os
3 import os
4 from rhodecode.lib import vcs
4 from rhodecode.lib import vcs
5
5
6 from rhodecode.model.db import Repository, RepoGroup
6 from rhodecode.model.db import Repository, RepoGroup
7 from rhodecode.tests import *
7 from rhodecode.tests import *
8 from rhodecode.model.repos_group import ReposGroupModel
8 from rhodecode.model.repos_group import ReposGroupModel
9 from rhodecode.model.repo import RepoModel
9 from rhodecode.model.repo import RepoModel
10
10
11
11
12 class TestAdminReposController(TestController):
12 class TestAdminReposController(TestController):
13
13
14 def __make_repo(self):
14 def __make_repo(self):
15 pass
15 pass
16
16
17 def test_index(self):
17 def test_index(self):
18 self.log_user()
18 self.log_user()
19 response = self.app.get(url('repos'))
19 response = self.app.get(url('repos'))
20 # Test response...
20 # Test response...
21
21
22 def test_index_as_xml(self):
22 def test_index_as_xml(self):
23 response = self.app.get(url('formatted_repos', format='xml'))
23 response = self.app.get(url('formatted_repos', format='xml'))
24
24
25 def test_create_hg(self):
25 def test_create_hg(self):
26 self.log_user()
26 self.log_user()
27 repo_name = NEW_HG_REPO
27 repo_name = NEW_HG_REPO
28 description = 'description for newly created repo'
28 description = 'description for newly created repo'
29 private = False
29 response = self.app.post(url('repos'),
30 response = self.app.post(url('repos'), {'repo_name': repo_name,
30 _get_repo_create_params(repo_private=False,
31 'repo_type': 'hg',
31 repo_name=repo_name,
32 'clone_uri': '',
32 repo_description=description))
33 'repo_group': '',
34 'description': description,
35 'private': private,
36 'landing_rev': 'tip'})
37 self.checkSessionFlash(response,
33 self.checkSessionFlash(response,
38 'created repository %s' % (repo_name))
34 'created repository %s' % (repo_name))
39
35
40 #test if the repo was created in the database
36 #test if the repo was created in the database
41 new_repo = self.Session().query(Repository)\
37 new_repo = self.Session().query(Repository)\
42 .filter(Repository.repo_name == repo_name).one()
38 .filter(Repository.repo_name == repo_name).one()
43
39
44 self.assertEqual(new_repo.repo_name, repo_name)
40 self.assertEqual(new_repo.repo_name, repo_name)
45 self.assertEqual(new_repo.description, description)
41 self.assertEqual(new_repo.description, description)
46
42
47 #test if repository is visible in the list ?
43 #test if repository is visible in the list ?
48 response = response.follow()
44 response = response.follow()
49
45
50 response.mustcontain(repo_name)
46 response.mustcontain(repo_name)
51
47
52 #test if repository was created on filesystem
48 #test if repository was created on filesystem
53 try:
49 try:
54 vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
50 vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
55 except:
51 except:
56 self.fail('no repo %s in filesystem' % repo_name)
52 self.fail('no repo %s in filesystem' % repo_name)
57
53
58 def test_create_hg_non_ascii(self):
54 def test_create_hg_non_ascii(self):
59 self.log_user()
55 self.log_user()
60 non_ascii = "Δ…Δ™Ε‚"
56 non_ascii = "Δ…Δ™Ε‚"
61 repo_name = "%s%s" % (NEW_HG_REPO, non_ascii)
57 repo_name = "%s%s" % (NEW_HG_REPO, non_ascii)
62 repo_name_unicode = repo_name.decode('utf8')
58 repo_name_unicode = repo_name.decode('utf8')
63 description = 'description for newly created repo' + non_ascii
59 description = 'description for newly created repo' + non_ascii
64 description_unicode = description.decode('utf8')
60 description_unicode = description.decode('utf8')
65 private = False
61 private = False
66 response = self.app.post(url('repos'), {'repo_name': repo_name,
62 response = self.app.post(url('repos'),
67 'repo_type': 'hg',
63 _get_repo_create_params(repo_private=False,
68 'clone_uri': '',
64 repo_name=repo_name,
69 'repo_group': '',
65 repo_description=description))
70 'description': description,
71 'private': private,
72 'landing_rev': 'tip'})
73 self.checkSessionFlash(response,
66 self.checkSessionFlash(response,
74 'created repository %s' % (repo_name_unicode))
67 'created repository %s' % (repo_name_unicode))
75
68
76 #test if the repo was created in the database
69 #test if the repo was created in the database
77 new_repo = self.Session().query(Repository)\
70 new_repo = self.Session().query(Repository)\
78 .filter(Repository.repo_name == repo_name_unicode).one()
71 .filter(Repository.repo_name == repo_name_unicode).one()
79
72
80 self.assertEqual(new_repo.repo_name, repo_name_unicode)
73 self.assertEqual(new_repo.repo_name, repo_name_unicode)
81 self.assertEqual(new_repo.description, description_unicode)
74 self.assertEqual(new_repo.description, description_unicode)
82
75
83 #test if repository is visible in the list ?
76 #test if repository is visible in the list ?
84 response = response.follow()
77 response = response.follow()
85
78
86 response.mustcontain(repo_name)
79 response.mustcontain(repo_name)
87
80
88 #test if repository was created on filesystem
81 #test if repository was created on filesystem
89 try:
82 try:
90 vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
83 vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
91 except:
84 except:
92 self.fail('no repo %s in filesystem' % repo_name)
85 self.fail('no repo %s in filesystem' % repo_name)
93
86
94 def test_create_hg_in_group(self):
87 def test_create_hg_in_group(self):
95 self.log_user()
88 self.log_user()
96
89
97 ## create GROUP
90 ## create GROUP
98 group_name = 'sometest'
91 group_name = 'sometest'
99 gr = ReposGroupModel().create(group_name=group_name,
92 gr = ReposGroupModel().create(group_name=group_name,
100 group_description='test',)
93 group_description='test',)
101 self.Session().commit()
94 self.Session().commit()
102
95
103 repo_name = 'ingroup'
96 repo_name = 'ingroup'
104 repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
97 repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
105 description = 'description for newly created repo'
98 description = 'description for newly created repo'
106 private = False
99 response = self.app.post(url('repos'),
107 response = self.app.post(url('repos'), {'repo_name': repo_name,
100 _get_repo_create_params(repo_private=False,
108 'repo_type': 'hg',
101 repo_name=repo_name,
109 'clone_uri': '',
102 repo_description=description,
110 'repo_group': gr.group_id,
103 repo_group=gr.group_id,))
111 'description': description,
104
112 'private': private,
113 'landing_rev': 'tip'})
114 self.checkSessionFlash(response,
105 self.checkSessionFlash(response,
115 'created repository %s' % (repo_name))
106 'created repository %s' % (repo_name))
116
107
117 #test if the repo was created in the database
108 #test if the repo was created in the database
118 new_repo = self.Session().query(Repository)\
109 new_repo = self.Session().query(Repository)\
119 .filter(Repository.repo_name == repo_name_full).one()
110 .filter(Repository.repo_name == repo_name_full).one()
120
111
121 self.assertEqual(new_repo.repo_name, repo_name_full)
112 self.assertEqual(new_repo.repo_name, repo_name_full)
122 self.assertEqual(new_repo.description, description)
113 self.assertEqual(new_repo.description, description)
123
114
124 #test if repository is visible in the list ?
115 #test if repository is visible in the list ?
125 response = response.follow()
116 response = response.follow()
126
117
127 response.mustcontain(repo_name_full)
118 response.mustcontain(repo_name_full)
128
119
129 #test if repository was created on filesystem
120 #test if repository was created on filesystem
130 try:
121 try:
131 vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name_full))
122 vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name_full))
132 except:
123 except:
133 ReposGroupModel().delete(group_name)
124 ReposGroupModel().delete(group_name)
134 self.Session().commit()
125 self.Session().commit()
135 self.fail('no repo %s in filesystem' % repo_name)
126 self.fail('no repo %s in filesystem' % repo_name)
136
127
137 RepoModel().delete(repo_name_full)
128 RepoModel().delete(repo_name_full)
138 ReposGroupModel().delete(group_name)
129 ReposGroupModel().delete(group_name)
139 self.Session().commit()
130 self.Session().commit()
140
131
141 def test_create_git(self):
132 def test_create_git(self):
142 self.log_user()
133 self.log_user()
143 repo_name = NEW_GIT_REPO
134 repo_name = NEW_GIT_REPO
144 description = 'description for newly created repo'
135 description = 'description for newly created repo'
145 private = False
136
146 response = self.app.post(url('repos'), {'repo_name': repo_name,
137 response = self.app.post(url('repos'),
147 'repo_type': 'git',
138 _get_repo_create_params(repo_private=False,
148 'clone_uri': '',
139 repo_type='git',
149 'repo_group': '',
140 repo_name=repo_name,
150 'description': description,
141 repo_description=description))
151 'private': private,
152 'landing_rev': 'tip'})
153 self.checkSessionFlash(response,
142 self.checkSessionFlash(response,
154 'created repository %s' % (repo_name))
143 'created repository %s' % (repo_name))
155
144
156 #test if the repo was created in the database
145 #test if the repo was created in the database
157 new_repo = self.Session().query(Repository)\
146 new_repo = self.Session().query(Repository)\
158 .filter(Repository.repo_name == repo_name).one()
147 .filter(Repository.repo_name == repo_name).one()
159
148
160 self.assertEqual(new_repo.repo_name, repo_name)
149 self.assertEqual(new_repo.repo_name, repo_name)
161 self.assertEqual(new_repo.description, description)
150 self.assertEqual(new_repo.description, description)
162
151
163 #test if repository is visible in the list ?
152 #test if repository is visible in the list ?
164 response = response.follow()
153 response = response.follow()
165
154
166 response.mustcontain(repo_name)
155 response.mustcontain(repo_name)
167
156
168 #test if repository was created on filesystem
157 #test if repository was created on filesystem
169 try:
158 try:
170 vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
159 vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
171 except:
160 except:
172 self.fail('no repo %s in filesystem' % repo_name)
161 self.fail('no repo %s in filesystem' % repo_name)
173
162
174 def test_create_git_non_ascii(self):
163 def test_create_git_non_ascii(self):
175 self.log_user()
164 self.log_user()
176 non_ascii = "Δ…Δ™Ε‚"
165 non_ascii = "Δ…Δ™Ε‚"
177 repo_name = "%s%s" % (NEW_GIT_REPO, non_ascii)
166 repo_name = "%s%s" % (NEW_GIT_REPO, non_ascii)
178 repo_name_unicode = repo_name.decode('utf8')
167 repo_name_unicode = repo_name.decode('utf8')
179 description = 'description for newly created repo' + non_ascii
168 description = 'description for newly created repo' + non_ascii
180 description_unicode = description.decode('utf8')
169 description_unicode = description.decode('utf8')
181 private = False
170 private = False
182 response = self.app.post(url('repos'), {'repo_name': repo_name,
171 response = self.app.post(url('repos'),
183 'repo_type': 'git',
172 _get_repo_create_params(repo_private=False,
184 'clone_uri': '',
173 repo_type='git',
185 'repo_group': '',
174 repo_name=repo_name,
186 'description': description,
175 repo_description=description))
187 'private': private,
176
188 'landing_rev': 'tip'})
189 self.checkSessionFlash(response,
177 self.checkSessionFlash(response,
190 'created repository %s' % (repo_name_unicode))
178 'created repository %s' % (repo_name_unicode))
191
179
192 #test if the repo was created in the database
180 #test if the repo was created in the database
193 new_repo = self.Session().query(Repository)\
181 new_repo = self.Session().query(Repository)\
194 .filter(Repository.repo_name == repo_name_unicode).one()
182 .filter(Repository.repo_name == repo_name_unicode).one()
195
183
196 self.assertEqual(new_repo.repo_name, repo_name_unicode)
184 self.assertEqual(new_repo.repo_name, repo_name_unicode)
197 self.assertEqual(new_repo.description, description_unicode)
185 self.assertEqual(new_repo.description, description_unicode)
198
186
199 #test if repository is visible in the list ?
187 #test if repository is visible in the list ?
200 response = response.follow()
188 response = response.follow()
201
189
202 response.mustcontain(repo_name)
190 response.mustcontain(repo_name)
203
191
204 #test if repository was created on filesystem
192 #test if repository was created on filesystem
205 try:
193 try:
206 vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
194 vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
207 except:
195 except:
208 self.fail('no repo %s in filesystem' % repo_name)
196 self.fail('no repo %s in filesystem' % repo_name)
209
197
210 def test_new(self):
198 def test_new(self):
211 self.log_user()
199 self.log_user()
212 response = self.app.get(url('new_repo'))
200 response = self.app.get(url('new_repo'))
213
201
214 def test_new_as_xml(self):
202 def test_new_as_xml(self):
215 response = self.app.get(url('formatted_new_repo', format='xml'))
203 response = self.app.get(url('formatted_new_repo', format='xml'))
216
204
217 def test_update(self):
205 def test_update(self):
218 response = self.app.put(url('repo', repo_name=HG_REPO))
206 response = self.app.put(url('repo', repo_name=HG_REPO))
219
207
220 def test_update_browser_fakeout(self):
208 def test_update_browser_fakeout(self):
221 response = self.app.post(url('repo', repo_name=HG_REPO),
209 response = self.app.post(url('repo', repo_name=HG_REPO),
222 params=dict(_method='put'))
210 params=dict(_method='put'))
223
211
224 def test_delete_hg(self):
212 def test_delete_hg(self):
225 self.log_user()
213 self.log_user()
226 repo_name = 'vcs_test_new_to_delete'
214 repo_name = 'vcs_test_new_to_delete'
227 description = 'description for newly created repo'
215 description = 'description for newly created repo'
228 private = False
216 private = False
229 response = self.app.post(url('repos'), {'repo_name': repo_name,
217 response = self.app.post(url('repos'),
230 'repo_type': 'hg',
218 _get_repo_create_params(repo_private=False,
231 'clone_uri': '',
219 repo_type='hg',
232 'repo_group': '',
220 repo_name=repo_name,
233 'description': description,
221 repo_description=description))
234 'private': private,
222
235 'landing_rev': 'tip'})
236 self.checkSessionFlash(response,
223 self.checkSessionFlash(response,
237 'created repository %s' % (repo_name))
224 'created repository %s' % (repo_name))
238
225
239 #test if the repo was created in the database
226 #test if the repo was created in the database
240 new_repo = self.Session().query(Repository)\
227 new_repo = self.Session().query(Repository)\
241 .filter(Repository.repo_name == repo_name).one()
228 .filter(Repository.repo_name == repo_name).one()
242
229
243 self.assertEqual(new_repo.repo_name, repo_name)
230 self.assertEqual(new_repo.repo_name, repo_name)
244 self.assertEqual(new_repo.description, description)
231 self.assertEqual(new_repo.description, description)
245
232
246 #test if repository is visible in the list ?
233 #test if repository is visible in the list ?
247 response = response.follow()
234 response = response.follow()
248
235
249 response.mustcontain(repo_name)
236 response.mustcontain(repo_name)
250
237
251 #test if repository was created on filesystem
238 #test if repository was created on filesystem
252 try:
239 try:
253 vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
240 vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
254 except:
241 except:
255 self.fail('no repo %s in filesystem' % repo_name)
242 self.fail('no repo %s in filesystem' % repo_name)
256
243
257 response = self.app.delete(url('repo', repo_name=repo_name))
244 response = self.app.delete(url('repo', repo_name=repo_name))
258
245
259 self.assertTrue('''deleted repository %s''' % (repo_name) in
246 self.assertTrue('''deleted repository %s''' % (repo_name) in
260 response.session['flash'][0])
247 response.session['flash'][0])
261
248
262 response.follow()
249 response.follow()
263
250
264 #check if repo was deleted from db
251 #check if repo was deleted from db
265 deleted_repo = self.Session().query(Repository)\
252 deleted_repo = self.Session().query(Repository)\
266 .filter(Repository.repo_name == repo_name).scalar()
253 .filter(Repository.repo_name == repo_name).scalar()
267
254
268 self.assertEqual(deleted_repo, None)
255 self.assertEqual(deleted_repo, None)
269
256
270 self.assertEqual(os.path.isdir(os.path.join(TESTS_TMP_PATH, repo_name)),
257 self.assertEqual(os.path.isdir(os.path.join(TESTS_TMP_PATH, repo_name)),
271 False)
258 False)
272
259
273 def test_delete_git(self):
260 def test_delete_git(self):
274 self.log_user()
261 self.log_user()
275 repo_name = 'vcs_test_new_to_delete'
262 repo_name = 'vcs_test_new_to_delete'
276 description = 'description for newly created repo'
263 description = 'description for newly created repo'
277 private = False
264 private = False
278 response = self.app.post(url('repos'), {'repo_name': repo_name,
265 response = self.app.post(url('repos'),
279 'repo_type': 'git',
266 _get_repo_create_params(repo_private=False,
280 'clone_uri': '',
267 repo_type='git',
281 'repo_group': '',
268 repo_name=repo_name,
282 'description': description,
269 repo_description=description))
283 'private': private,
270
284 'landing_rev': 'tip'})
285 self.checkSessionFlash(response,
271 self.checkSessionFlash(response,
286 'created repository %s' % (repo_name))
272 'created repository %s' % (repo_name))
287
273
288 #test if the repo was created in the database
274 #test if the repo was created in the database
289 new_repo = self.Session().query(Repository)\
275 new_repo = self.Session().query(Repository)\
290 .filter(Repository.repo_name == repo_name).one()
276 .filter(Repository.repo_name == repo_name).one()
291
277
292 self.assertEqual(new_repo.repo_name, repo_name)
278 self.assertEqual(new_repo.repo_name, repo_name)
293 self.assertEqual(new_repo.description, description)
279 self.assertEqual(new_repo.description, description)
294
280
295 #test if repository is visible in the list ?
281 #test if repository is visible in the list ?
296 response = response.follow()
282 response = response.follow()
297
283
298 response.mustcontain(repo_name)
284 response.mustcontain(repo_name)
299
285
300 #test if repository was created on filesystem
286 #test if repository was created on filesystem
301 try:
287 try:
302 vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
288 vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
303 except:
289 except:
304 self.fail('no repo %s in filesystem' % repo_name)
290 self.fail('no repo %s in filesystem' % repo_name)
305
291
306 response = self.app.delete(url('repo', repo_name=repo_name))
292 response = self.app.delete(url('repo', repo_name=repo_name))
307
293
308 self.assertTrue('''deleted repository %s''' % (repo_name) in
294 self.assertTrue('''deleted repository %s''' % (repo_name) in
309 response.session['flash'][0])
295 response.session['flash'][0])
310
296
311 response.follow()
297 response.follow()
312
298
313 #check if repo was deleted from db
299 #check if repo was deleted from db
314 deleted_repo = self.Session().query(Repository)\
300 deleted_repo = self.Session().query(Repository)\
315 .filter(Repository.repo_name == repo_name).scalar()
301 .filter(Repository.repo_name == repo_name).scalar()
316
302
317 self.assertEqual(deleted_repo, None)
303 self.assertEqual(deleted_repo, None)
318
304
319 self.assertEqual(os.path.isdir(os.path.join(TESTS_TMP_PATH, repo_name)),
305 self.assertEqual(os.path.isdir(os.path.join(TESTS_TMP_PATH, repo_name)),
320 False)
306 False)
321
307
322 def test_delete_repo_with_group(self):
308 def test_delete_repo_with_group(self):
323 #TODO:
309 #TODO:
324 pass
310 pass
325
311
326 def test_delete_browser_fakeout(self):
312 def test_delete_browser_fakeout(self):
327 response = self.app.post(url('repo', repo_name=HG_REPO),
313 response = self.app.post(url('repo', repo_name=HG_REPO),
328 params=dict(_method='delete'))
314 params=dict(_method='delete'))
329
315
330 def test_show_hg(self):
316 def test_show_hg(self):
331 self.log_user()
317 self.log_user()
332 response = self.app.get(url('repo', repo_name=HG_REPO))
318 response = self.app.get(url('repo', repo_name=HG_REPO))
333
319
334 def test_show_git(self):
320 def test_show_git(self):
335 self.log_user()
321 self.log_user()
336 response = self.app.get(url('repo', repo_name=GIT_REPO))
322 response = self.app.get(url('repo', repo_name=GIT_REPO))
337
323
338
324
339 def test_edit(self):
325 def test_edit(self):
340 response = self.app.get(url('edit_repo', repo_name=HG_REPO))
326 response = self.app.get(url('edit_repo', repo_name=HG_REPO))
@@ -1,348 +1,357 b''
1 from rhodecode.tests import *
1 from rhodecode.tests import *
2 from rhodecode.model.db import Repository
3 from rhodecode.model.meta import Session
2
4
3 ARCHIVE_SPECS = {
5 ARCHIVE_SPECS = {
4 '.tar.bz2': ('application/x-bzip2', 'tbz2', ''),
6 '.tar.bz2': ('application/x-bzip2', 'tbz2', ''),
5 '.tar.gz': ('application/x-gzip', 'tgz', ''),
7 '.tar.gz': ('application/x-gzip', 'tgz', ''),
6 '.zip': ('application/zip', 'zip', ''),
8 '.zip': ('application/zip', 'zip', ''),
7 }
9 }
8
10
9
11
12 def _set_downloads(repo_name, set_to):
13 repo = Repository.get_by_repo_name(repo_name)
14 repo.enable_downloads = set_to
15 Session().add(repo)
16 Session().commit()
17
18
10 class TestFilesController(TestController):
19 class TestFilesController(TestController):
11
20
12 def test_index(self):
21 def test_index(self):
13 self.log_user()
22 self.log_user()
14 response = self.app.get(url(controller='files', action='index',
23 response = self.app.get(url(controller='files', action='index',
15 repo_name=HG_REPO,
24 repo_name=HG_REPO,
16 revision='tip',
25 revision='tip',
17 f_path='/'))
26 f_path='/'))
18 # Test response...
27 # Test response...
19 response.mustcontain('<a class="browser-dir ypjax-link" href="/vcs_test_hg/files/27cd5cce30c96924232dffcd24178a07ffeb5dfc/docs">docs</a>')
28 response.mustcontain('<a class="browser-dir ypjax-link" href="/vcs_test_hg/files/27cd5cce30c96924232dffcd24178a07ffeb5dfc/docs">docs</a>')
20 response.mustcontain('<a class="browser-dir ypjax-link" href="/vcs_test_hg/files/27cd5cce30c96924232dffcd24178a07ffeb5dfc/tests">tests</a>')
29 response.mustcontain('<a class="browser-dir ypjax-link" href="/vcs_test_hg/files/27cd5cce30c96924232dffcd24178a07ffeb5dfc/tests">tests</a>')
21 response.mustcontain('<a class="browser-dir ypjax-link" href="/vcs_test_hg/files/27cd5cce30c96924232dffcd24178a07ffeb5dfc/vcs">vcs</a>')
30 response.mustcontain('<a class="browser-dir ypjax-link" href="/vcs_test_hg/files/27cd5cce30c96924232dffcd24178a07ffeb5dfc/vcs">vcs</a>')
22 response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/27cd5cce30c96924232dffcd24178a07ffeb5dfc/.hgignore">.hgignore</a>')
31 response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/27cd5cce30c96924232dffcd24178a07ffeb5dfc/.hgignore">.hgignore</a>')
23 response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/27cd5cce30c96924232dffcd24178a07ffeb5dfc/MANIFEST.in">MANIFEST.in</a>')
32 response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/27cd5cce30c96924232dffcd24178a07ffeb5dfc/MANIFEST.in">MANIFEST.in</a>')
24
33
25 def test_index_revision(self):
34 def test_index_revision(self):
26 self.log_user()
35 self.log_user()
27
36
28 response = self.app.get(
37 response = self.app.get(
29 url(controller='files', action='index',
38 url(controller='files', action='index',
30 repo_name=HG_REPO,
39 repo_name=HG_REPO,
31 revision='7ba66bec8d6dbba14a2155be32408c435c5f4492',
40 revision='7ba66bec8d6dbba14a2155be32408c435c5f4492',
32 f_path='/')
41 f_path='/')
33 )
42 )
34
43
35 #Test response...
44 #Test response...
36
45
37 response.mustcontain('<a class="browser-dir ypjax-link" href="/vcs_test_hg/files/7ba66bec8d6dbba14a2155be32408c435c5f4492/docs">docs</a>')
46 response.mustcontain('<a class="browser-dir ypjax-link" href="/vcs_test_hg/files/7ba66bec8d6dbba14a2155be32408c435c5f4492/docs">docs</a>')
38 response.mustcontain('<a class="browser-dir ypjax-link" href="/vcs_test_hg/files/7ba66bec8d6dbba14a2155be32408c435c5f4492/tests">tests</a>')
47 response.mustcontain('<a class="browser-dir ypjax-link" href="/vcs_test_hg/files/7ba66bec8d6dbba14a2155be32408c435c5f4492/tests">tests</a>')
39 response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/7ba66bec8d6dbba14a2155be32408c435c5f4492/README.rst">README.rst</a>')
48 response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/7ba66bec8d6dbba14a2155be32408c435c5f4492/README.rst">README.rst</a>')
40 response.mustcontain('1.1 KiB')
49 response.mustcontain('1.1 KiB')
41 response.mustcontain('text/x-python')
50 response.mustcontain('text/x-python')
42
51
43 def test_index_different_branch(self):
52 def test_index_different_branch(self):
44 self.log_user()
53 self.log_user()
45
54
46 response = self.app.get(url(controller='files', action='index',
55 response = self.app.get(url(controller='files', action='index',
47 repo_name=HG_REPO,
56 repo_name=HG_REPO,
48 revision='97e8b885c04894463c51898e14387d80c30ed1ee',
57 revision='97e8b885c04894463c51898e14387d80c30ed1ee',
49 f_path='/'))
58 f_path='/'))
50
59
51 response.mustcontain("""<span style="text-transform: uppercase;"><a href="#">branch: git</a></span>""")
60 response.mustcontain("""<span style="text-transform: uppercase;"><a href="#">branch: git</a></span>""")
52
61
53 def test_index_paging(self):
62 def test_index_paging(self):
54 self.log_user()
63 self.log_user()
55
64
56 for r in [(73, 'a066b25d5df7016b45a41b7e2a78c33b57adc235'),
65 for r in [(73, 'a066b25d5df7016b45a41b7e2a78c33b57adc235'),
57 (92, 'cc66b61b8455b264a7a8a2d8ddc80fcfc58c221e'),
66 (92, 'cc66b61b8455b264a7a8a2d8ddc80fcfc58c221e'),
58 (109, '75feb4c33e81186c87eac740cee2447330288412'),
67 (109, '75feb4c33e81186c87eac740cee2447330288412'),
59 (1, '3d8f361e72ab303da48d799ff1ac40d5ac37c67e'),
68 (1, '3d8f361e72ab303da48d799ff1ac40d5ac37c67e'),
60 (0, 'b986218ba1c9b0d6a259fac9b050b1724ed8e545')]:
69 (0, 'b986218ba1c9b0d6a259fac9b050b1724ed8e545')]:
61
70
62 response = self.app.get(url(controller='files', action='index',
71 response = self.app.get(url(controller='files', action='index',
63 repo_name=HG_REPO,
72 repo_name=HG_REPO,
64 revision=r[1],
73 revision=r[1],
65 f_path='/'))
74 f_path='/'))
66
75
67 response.mustcontain("""@ r%s:%s""" % (r[0], r[1][:12]))
76 response.mustcontain("""@ r%s:%s""" % (r[0], r[1][:12]))
68
77
69 def test_file_source(self):
78 def test_file_source(self):
70 self.log_user()
79 self.log_user()
71 response = self.app.get(url(controller='files', action='index',
80 response = self.app.get(url(controller='files', action='index',
72 repo_name=HG_REPO,
81 repo_name=HG_REPO,
73 revision='27cd5cce30c96924232dffcd24178a07ffeb5dfc',
82 revision='27cd5cce30c96924232dffcd24178a07ffeb5dfc',
74 f_path='vcs/nodes.py'))
83 f_path='vcs/nodes.py'))
75
84
76 response.mustcontain("""<div class="commit">Partially implemented <a class="issue-tracker-link" href="https://myissueserver.com/vcs_test_hg/issue/16">#16</a>. filecontent/commit message/author/node name are safe_unicode now.
85 response.mustcontain("""<div class="commit">Partially implemented <a class="issue-tracker-link" href="https://myissueserver.com/vcs_test_hg/issue/16">#16</a>. filecontent/commit message/author/node name are safe_unicode now.
77 In addition some other __str__ are unicode as well
86 In addition some other __str__ are unicode as well
78 Added test for unicode
87 Added test for unicode
79 Improved test to clone into uniq repository.
88 Improved test to clone into uniq repository.
80 removed extra unicode conversion in diff.</div>
89 removed extra unicode conversion in diff.</div>
81 """)
90 """)
82
91
83 response.mustcontain("""<span style="text-transform: uppercase;"><a href="#">branch: default</a></span>""")
92 response.mustcontain("""<span style="text-transform: uppercase;"><a href="#">branch: default</a></span>""")
84
93
85 def test_file_source_history(self):
94 def test_file_source_history(self):
86 self.log_user()
95 self.log_user()
87 response = self.app.get(url(controller='files', action='history',
96 response = self.app.get(url(controller='files', action='history',
88 repo_name=HG_REPO,
97 repo_name=HG_REPO,
89 revision='27cd5cce30c96924232dffcd24178a07ffeb5dfc',
98 revision='27cd5cce30c96924232dffcd24178a07ffeb5dfc',
90 f_path='vcs/nodes.py'),
99 f_path='vcs/nodes.py'),
91 extra_environ={'HTTP_X_PARTIAL_XHR': '1'},)
100 extra_environ={'HTTP_X_PARTIAL_XHR': '1'},)
92 #test or history
101 #test or history
93 response.mustcontain("""<optgroup label="Changesets">
102 response.mustcontain("""<optgroup label="Changesets">
94 <option selected="selected" value="8911406ad776fdd3d0b9932a2e89677e57405a48">r167:8911406ad776 (default)</option>
103 <option selected="selected" value="8911406ad776fdd3d0b9932a2e89677e57405a48">r167:8911406ad776 (default)</option>
95 <option value="aa957ed78c35a1541f508d2ec90e501b0a9e3167">r165:aa957ed78c35 (default)</option>
104 <option value="aa957ed78c35a1541f508d2ec90e501b0a9e3167">r165:aa957ed78c35 (default)</option>
96 <option value="48e11b73e94c0db33e736eaeea692f990cb0b5f1">r140:48e11b73e94c (default)</option>
105 <option value="48e11b73e94c0db33e736eaeea692f990cb0b5f1">r140:48e11b73e94c (default)</option>
97 <option value="adf3cbf483298563b968a6c673cd5bde5f7d5eea">r126:adf3cbf48329 (default)</option>
106 <option value="adf3cbf483298563b968a6c673cd5bde5f7d5eea">r126:adf3cbf48329 (default)</option>
98 <option value="6249fd0fb2cfb1411e764129f598e2cf0de79a6f">r113:6249fd0fb2cf (git)</option>
107 <option value="6249fd0fb2cfb1411e764129f598e2cf0de79a6f">r113:6249fd0fb2cf (git)</option>
99 <option value="75feb4c33e81186c87eac740cee2447330288412">r109:75feb4c33e81 (default)</option>
108 <option value="75feb4c33e81186c87eac740cee2447330288412">r109:75feb4c33e81 (default)</option>
100 <option value="9a4dc232ecdc763ef2e98ae2238cfcbba4f6ad8d">r108:9a4dc232ecdc (default)</option>
109 <option value="9a4dc232ecdc763ef2e98ae2238cfcbba4f6ad8d">r108:9a4dc232ecdc (default)</option>
101 <option value="595cce4efa21fda2f2e4eeb4fe5f2a6befe6fa2d">r107:595cce4efa21 (default)</option>
110 <option value="595cce4efa21fda2f2e4eeb4fe5f2a6befe6fa2d">r107:595cce4efa21 (default)</option>
102 <option value="4a8bd421fbc2dfbfb70d85a3fe064075ab2c49da">r104:4a8bd421fbc2 (default)</option>
111 <option value="4a8bd421fbc2dfbfb70d85a3fe064075ab2c49da">r104:4a8bd421fbc2 (default)</option>
103 <option value="57be63fc8f85e65a0106a53187f7316f8c487ffa">r102:57be63fc8f85 (default)</option>
112 <option value="57be63fc8f85e65a0106a53187f7316f8c487ffa">r102:57be63fc8f85 (default)</option>
104 <option value="5530bd87f7e2e124a64d07cb2654c997682128be">r101:5530bd87f7e2 (git)</option>
113 <option value="5530bd87f7e2e124a64d07cb2654c997682128be">r101:5530bd87f7e2 (git)</option>
105 <option value="e516008b1c93f142263dc4b7961787cbad654ce1">r99:e516008b1c93 (default)</option>
114 <option value="e516008b1c93f142263dc4b7961787cbad654ce1">r99:e516008b1c93 (default)</option>
106 <option value="41f43fc74b8b285984554532eb105ac3be5c434f">r93:41f43fc74b8b (default)</option>
115 <option value="41f43fc74b8b285984554532eb105ac3be5c434f">r93:41f43fc74b8b (default)</option>
107 <option value="cc66b61b8455b264a7a8a2d8ddc80fcfc58c221e">r92:cc66b61b8455 (default)</option>
116 <option value="cc66b61b8455b264a7a8a2d8ddc80fcfc58c221e">r92:cc66b61b8455 (default)</option>
108 <option value="73ab5b616b3271b0518682fb4988ce421de8099f">r91:73ab5b616b32 (default)</option>
117 <option value="73ab5b616b3271b0518682fb4988ce421de8099f">r91:73ab5b616b32 (default)</option>
109 <option value="e0da75f308c0f18f98e9ce6257626009fdda2b39">r82:e0da75f308c0 (default)</option>
118 <option value="e0da75f308c0f18f98e9ce6257626009fdda2b39">r82:e0da75f308c0 (default)</option>
110 <option value="fb2e41e0f0810be4d7103bc2a4c7be16ee3ec611">r81:fb2e41e0f081 (default)</option>
119 <option value="fb2e41e0f0810be4d7103bc2a4c7be16ee3ec611">r81:fb2e41e0f081 (default)</option>
111 <option value="602ae2f5e7ade70b3b66a58cdd9e3e613dc8a028">r76:602ae2f5e7ad (default)</option>
120 <option value="602ae2f5e7ade70b3b66a58cdd9e3e613dc8a028">r76:602ae2f5e7ad (default)</option>
112 <option value="a066b25d5df7016b45a41b7e2a78c33b57adc235">r73:a066b25d5df7 (default)</option>
121 <option value="a066b25d5df7016b45a41b7e2a78c33b57adc235">r73:a066b25d5df7 (default)</option>
113 <option value="637a933c905958ce5151f154147c25c1c7b68832">r61:637a933c9059 (web)</option>
122 <option value="637a933c905958ce5151f154147c25c1c7b68832">r61:637a933c9059 (web)</option>
114 <option value="0c21004effeb8ce2d2d5b4a8baf6afa8394b6fbc">r60:0c21004effeb (web)</option>
123 <option value="0c21004effeb8ce2d2d5b4a8baf6afa8394b6fbc">r60:0c21004effeb (web)</option>
115 <option value="a1f39c56d3f1d52d5fb5920370a2a2716cd9a444">r59:a1f39c56d3f1 (web)</option>
124 <option value="a1f39c56d3f1d52d5fb5920370a2a2716cd9a444">r59:a1f39c56d3f1 (web)</option>
116 <option value="97d32df05c715a3bbf936bf3cc4e32fb77fe1a7f">r58:97d32df05c71 (web)</option>
125 <option value="97d32df05c715a3bbf936bf3cc4e32fb77fe1a7f">r58:97d32df05c71 (web)</option>
117 <option value="08eaf14517718dccea4b67755a93368341aca919">r57:08eaf1451771 (web)</option>
126 <option value="08eaf14517718dccea4b67755a93368341aca919">r57:08eaf1451771 (web)</option>
118 <option value="22f71ad265265a53238359c883aa976e725aa07d">r56:22f71ad26526 (web)</option>
127 <option value="22f71ad265265a53238359c883aa976e725aa07d">r56:22f71ad26526 (web)</option>
119 <option value="97501f02b7b4330924b647755663a2d90a5e638d">r49:97501f02b7b4 (web)</option>
128 <option value="97501f02b7b4330924b647755663a2d90a5e638d">r49:97501f02b7b4 (web)</option>
120 <option value="86ede6754f2b27309452bb11f997386ae01d0e5a">r47:86ede6754f2b (web)</option>
129 <option value="86ede6754f2b27309452bb11f997386ae01d0e5a">r47:86ede6754f2b (web)</option>
121 <option value="014c40c0203c423dc19ecf94644f7cac9d4cdce0">r45:014c40c0203c (web)</option>
130 <option value="014c40c0203c423dc19ecf94644f7cac9d4cdce0">r45:014c40c0203c (web)</option>
122 <option value="ee87846a61c12153b51543bf860e1026c6d3dcba">r30:ee87846a61c1 (default)</option>
131 <option value="ee87846a61c12153b51543bf860e1026c6d3dcba">r30:ee87846a61c1 (default)</option>
123 <option value="9bb326a04ae5d98d437dece54be04f830cf1edd9">r26:9bb326a04ae5 (default)</option>
132 <option value="9bb326a04ae5d98d437dece54be04f830cf1edd9">r26:9bb326a04ae5 (default)</option>
124 <option value="536c1a19428381cfea92ac44985304f6a8049569">r24:536c1a194283 (default)</option>
133 <option value="536c1a19428381cfea92ac44985304f6a8049569">r24:536c1a194283 (default)</option>
125 <option value="dc5d2c0661b61928834a785d3e64a3f80d3aad9c">r8:dc5d2c0661b6 (default)</option>
134 <option value="dc5d2c0661b61928834a785d3e64a3f80d3aad9c">r8:dc5d2c0661b6 (default)</option>
126 <option value="3803844fdbd3b711175fc3da9bdacfcd6d29a6fb">r7:3803844fdbd3 (default)</option>
135 <option value="3803844fdbd3b711175fc3da9bdacfcd6d29a6fb">r7:3803844fdbd3 (default)</option>
127 </optgroup>
136 </optgroup>
128 <optgroup label="Branches">
137 <optgroup label="Branches">
129 <option value="27cd5cce30c96924232dffcd24178a07ffeb5dfc">default</option>
138 <option value="27cd5cce30c96924232dffcd24178a07ffeb5dfc">default</option>
130 <option value="97e8b885c04894463c51898e14387d80c30ed1ee">git</option>
139 <option value="97e8b885c04894463c51898e14387d80c30ed1ee">git</option>
131 <option value="2e6a2bf9356ca56df08807f4ad86d480da72a8f4">web</option>
140 <option value="2e6a2bf9356ca56df08807f4ad86d480da72a8f4">web</option>
132 </optgroup>
141 </optgroup>
133 <optgroup label="Tags">
142 <optgroup label="Tags">
134 <option value="27cd5cce30c96924232dffcd24178a07ffeb5dfc">tip</option>
143 <option value="27cd5cce30c96924232dffcd24178a07ffeb5dfc">tip</option>
135 <option value="fd4bdb5e9b2a29b4393a4ac6caef48c17ee1a200">0.1.4</option>
144 <option value="fd4bdb5e9b2a29b4393a4ac6caef48c17ee1a200">0.1.4</option>
136 <option value="17544fbfcd33ffb439e2b728b5d526b1ef30bfcf">0.1.3</option>
145 <option value="17544fbfcd33ffb439e2b728b5d526b1ef30bfcf">0.1.3</option>
137 <option value="a7e60bff65d57ac3a1a1ce3b12a70f8a9e8a7720">0.1.2</option>
146 <option value="a7e60bff65d57ac3a1a1ce3b12a70f8a9e8a7720">0.1.2</option>
138 <option value="eb3a60fc964309c1a318b8dfe26aa2d1586c85ae">0.1.1</option>
147 <option value="eb3a60fc964309c1a318b8dfe26aa2d1586c85ae">0.1.1</option>
139 </optgroup>
148 </optgroup>
140 """)
149 """)
141
150
142 def test_file_annotation(self):
151 def test_file_annotation(self):
143 self.log_user()
152 self.log_user()
144 response = self.app.get(url(controller='files', action='index',
153 response = self.app.get(url(controller='files', action='index',
145 repo_name=HG_REPO,
154 repo_name=HG_REPO,
146 revision='27cd5cce30c96924232dffcd24178a07ffeb5dfc',
155 revision='27cd5cce30c96924232dffcd24178a07ffeb5dfc',
147 f_path='vcs/nodes.py',
156 f_path='vcs/nodes.py',
148 annotate=True))
157 annotate=True))
149
158
150 response.mustcontain("""<span style="text-transform: uppercase;"><a href="#">branch: default</a></span>""")
159 response.mustcontain("""<span style="text-transform: uppercase;"><a href="#">branch: default</a></span>""")
151
160
152 def test_file_annotation_history(self):
161 def test_file_annotation_history(self):
153 self.log_user()
162 self.log_user()
154 response = self.app.get(url(controller='files', action='history',
163 response = self.app.get(url(controller='files', action='history',
155 repo_name=HG_REPO,
164 repo_name=HG_REPO,
156 revision='27cd5cce30c96924232dffcd24178a07ffeb5dfc',
165 revision='27cd5cce30c96924232dffcd24178a07ffeb5dfc',
157 f_path='vcs/nodes.py',
166 f_path='vcs/nodes.py',
158 annotate=True),
167 annotate=True),
159 extra_environ={'HTTP_X_PARTIAL_XHR': '1'})
168 extra_environ={'HTTP_X_PARTIAL_XHR': '1'})
160
169
161 response.mustcontain("""<optgroup label="Changesets">
170 response.mustcontain("""<optgroup label="Changesets">
162 <option selected="selected" value="8911406ad776fdd3d0b9932a2e89677e57405a48">r167:8911406ad776 (default)</option>
171 <option selected="selected" value="8911406ad776fdd3d0b9932a2e89677e57405a48">r167:8911406ad776 (default)</option>
163 <option value="aa957ed78c35a1541f508d2ec90e501b0a9e3167">r165:aa957ed78c35 (default)</option>
172 <option value="aa957ed78c35a1541f508d2ec90e501b0a9e3167">r165:aa957ed78c35 (default)</option>
164 <option value="48e11b73e94c0db33e736eaeea692f990cb0b5f1">r140:48e11b73e94c (default)</option>
173 <option value="48e11b73e94c0db33e736eaeea692f990cb0b5f1">r140:48e11b73e94c (default)</option>
165 <option value="adf3cbf483298563b968a6c673cd5bde5f7d5eea">r126:adf3cbf48329 (default)</option>
174 <option value="adf3cbf483298563b968a6c673cd5bde5f7d5eea">r126:adf3cbf48329 (default)</option>
166 <option value="6249fd0fb2cfb1411e764129f598e2cf0de79a6f">r113:6249fd0fb2cf (git)</option>
175 <option value="6249fd0fb2cfb1411e764129f598e2cf0de79a6f">r113:6249fd0fb2cf (git)</option>
167 <option value="75feb4c33e81186c87eac740cee2447330288412">r109:75feb4c33e81 (default)</option>
176 <option value="75feb4c33e81186c87eac740cee2447330288412">r109:75feb4c33e81 (default)</option>
168 <option value="9a4dc232ecdc763ef2e98ae2238cfcbba4f6ad8d">r108:9a4dc232ecdc (default)</option>
177 <option value="9a4dc232ecdc763ef2e98ae2238cfcbba4f6ad8d">r108:9a4dc232ecdc (default)</option>
169 <option value="595cce4efa21fda2f2e4eeb4fe5f2a6befe6fa2d">r107:595cce4efa21 (default)</option>
178 <option value="595cce4efa21fda2f2e4eeb4fe5f2a6befe6fa2d">r107:595cce4efa21 (default)</option>
170 <option value="4a8bd421fbc2dfbfb70d85a3fe064075ab2c49da">r104:4a8bd421fbc2 (default)</option>
179 <option value="4a8bd421fbc2dfbfb70d85a3fe064075ab2c49da">r104:4a8bd421fbc2 (default)</option>
171 <option value="57be63fc8f85e65a0106a53187f7316f8c487ffa">r102:57be63fc8f85 (default)</option>
180 <option value="57be63fc8f85e65a0106a53187f7316f8c487ffa">r102:57be63fc8f85 (default)</option>
172 <option value="5530bd87f7e2e124a64d07cb2654c997682128be">r101:5530bd87f7e2 (git)</option>
181 <option value="5530bd87f7e2e124a64d07cb2654c997682128be">r101:5530bd87f7e2 (git)</option>
173 <option value="e516008b1c93f142263dc4b7961787cbad654ce1">r99:e516008b1c93 (default)</option>
182 <option value="e516008b1c93f142263dc4b7961787cbad654ce1">r99:e516008b1c93 (default)</option>
174 <option value="41f43fc74b8b285984554532eb105ac3be5c434f">r93:41f43fc74b8b (default)</option>
183 <option value="41f43fc74b8b285984554532eb105ac3be5c434f">r93:41f43fc74b8b (default)</option>
175 <option value="cc66b61b8455b264a7a8a2d8ddc80fcfc58c221e">r92:cc66b61b8455 (default)</option>
184 <option value="cc66b61b8455b264a7a8a2d8ddc80fcfc58c221e">r92:cc66b61b8455 (default)</option>
176 <option value="73ab5b616b3271b0518682fb4988ce421de8099f">r91:73ab5b616b32 (default)</option>
185 <option value="73ab5b616b3271b0518682fb4988ce421de8099f">r91:73ab5b616b32 (default)</option>
177 <option value="e0da75f308c0f18f98e9ce6257626009fdda2b39">r82:e0da75f308c0 (default)</option>
186 <option value="e0da75f308c0f18f98e9ce6257626009fdda2b39">r82:e0da75f308c0 (default)</option>
178 <option value="fb2e41e0f0810be4d7103bc2a4c7be16ee3ec611">r81:fb2e41e0f081 (default)</option>
187 <option value="fb2e41e0f0810be4d7103bc2a4c7be16ee3ec611">r81:fb2e41e0f081 (default)</option>
179 <option value="602ae2f5e7ade70b3b66a58cdd9e3e613dc8a028">r76:602ae2f5e7ad (default)</option>
188 <option value="602ae2f5e7ade70b3b66a58cdd9e3e613dc8a028">r76:602ae2f5e7ad (default)</option>
180 <option value="a066b25d5df7016b45a41b7e2a78c33b57adc235">r73:a066b25d5df7 (default)</option>
189 <option value="a066b25d5df7016b45a41b7e2a78c33b57adc235">r73:a066b25d5df7 (default)</option>
181 <option value="637a933c905958ce5151f154147c25c1c7b68832">r61:637a933c9059 (web)</option>
190 <option value="637a933c905958ce5151f154147c25c1c7b68832">r61:637a933c9059 (web)</option>
182 <option value="0c21004effeb8ce2d2d5b4a8baf6afa8394b6fbc">r60:0c21004effeb (web)</option>
191 <option value="0c21004effeb8ce2d2d5b4a8baf6afa8394b6fbc">r60:0c21004effeb (web)</option>
183 <option value="a1f39c56d3f1d52d5fb5920370a2a2716cd9a444">r59:a1f39c56d3f1 (web)</option>
192 <option value="a1f39c56d3f1d52d5fb5920370a2a2716cd9a444">r59:a1f39c56d3f1 (web)</option>
184 <option value="97d32df05c715a3bbf936bf3cc4e32fb77fe1a7f">r58:97d32df05c71 (web)</option>
193 <option value="97d32df05c715a3bbf936bf3cc4e32fb77fe1a7f">r58:97d32df05c71 (web)</option>
185 <option value="08eaf14517718dccea4b67755a93368341aca919">r57:08eaf1451771 (web)</option>
194 <option value="08eaf14517718dccea4b67755a93368341aca919">r57:08eaf1451771 (web)</option>
186 <option value="22f71ad265265a53238359c883aa976e725aa07d">r56:22f71ad26526 (web)</option>
195 <option value="22f71ad265265a53238359c883aa976e725aa07d">r56:22f71ad26526 (web)</option>
187 <option value="97501f02b7b4330924b647755663a2d90a5e638d">r49:97501f02b7b4 (web)</option>
196 <option value="97501f02b7b4330924b647755663a2d90a5e638d">r49:97501f02b7b4 (web)</option>
188 <option value="86ede6754f2b27309452bb11f997386ae01d0e5a">r47:86ede6754f2b (web)</option>
197 <option value="86ede6754f2b27309452bb11f997386ae01d0e5a">r47:86ede6754f2b (web)</option>
189 <option value="014c40c0203c423dc19ecf94644f7cac9d4cdce0">r45:014c40c0203c (web)</option>
198 <option value="014c40c0203c423dc19ecf94644f7cac9d4cdce0">r45:014c40c0203c (web)</option>
190 <option value="ee87846a61c12153b51543bf860e1026c6d3dcba">r30:ee87846a61c1 (default)</option>
199 <option value="ee87846a61c12153b51543bf860e1026c6d3dcba">r30:ee87846a61c1 (default)</option>
191 <option value="9bb326a04ae5d98d437dece54be04f830cf1edd9">r26:9bb326a04ae5 (default)</option>
200 <option value="9bb326a04ae5d98d437dece54be04f830cf1edd9">r26:9bb326a04ae5 (default)</option>
192 <option value="536c1a19428381cfea92ac44985304f6a8049569">r24:536c1a194283 (default)</option>
201 <option value="536c1a19428381cfea92ac44985304f6a8049569">r24:536c1a194283 (default)</option>
193 <option value="dc5d2c0661b61928834a785d3e64a3f80d3aad9c">r8:dc5d2c0661b6 (default)</option>
202 <option value="dc5d2c0661b61928834a785d3e64a3f80d3aad9c">r8:dc5d2c0661b6 (default)</option>
194 <option value="3803844fdbd3b711175fc3da9bdacfcd6d29a6fb">r7:3803844fdbd3 (default)</option>
203 <option value="3803844fdbd3b711175fc3da9bdacfcd6d29a6fb">r7:3803844fdbd3 (default)</option>
195 </optgroup>
204 </optgroup>
196 <optgroup label="Branches">
205 <optgroup label="Branches">
197 <option value="27cd5cce30c96924232dffcd24178a07ffeb5dfc">default</option>
206 <option value="27cd5cce30c96924232dffcd24178a07ffeb5dfc">default</option>
198 <option value="97e8b885c04894463c51898e14387d80c30ed1ee">git</option>
207 <option value="97e8b885c04894463c51898e14387d80c30ed1ee">git</option>
199 <option value="2e6a2bf9356ca56df08807f4ad86d480da72a8f4">web</option>
208 <option value="2e6a2bf9356ca56df08807f4ad86d480da72a8f4">web</option>
200 </optgroup>
209 </optgroup>
201 <optgroup label="Tags">
210 <optgroup label="Tags">
202 <option value="27cd5cce30c96924232dffcd24178a07ffeb5dfc">tip</option>
211 <option value="27cd5cce30c96924232dffcd24178a07ffeb5dfc">tip</option>
203 <option value="fd4bdb5e9b2a29b4393a4ac6caef48c17ee1a200">0.1.4</option>
212 <option value="fd4bdb5e9b2a29b4393a4ac6caef48c17ee1a200">0.1.4</option>
204 <option value="17544fbfcd33ffb439e2b728b5d526b1ef30bfcf">0.1.3</option>
213 <option value="17544fbfcd33ffb439e2b728b5d526b1ef30bfcf">0.1.3</option>
205 <option value="a7e60bff65d57ac3a1a1ce3b12a70f8a9e8a7720">0.1.2</option>
214 <option value="a7e60bff65d57ac3a1a1ce3b12a70f8a9e8a7720">0.1.2</option>
206 <option value="eb3a60fc964309c1a318b8dfe26aa2d1586c85ae">0.1.1</option>
215 <option value="eb3a60fc964309c1a318b8dfe26aa2d1586c85ae">0.1.1</option>
207 </optgroup>""")
216 </optgroup>""")
208
217
209 def test_file_annotation_git(self):
218 def test_file_annotation_git(self):
210 self.log_user()
219 self.log_user()
211 response = self.app.get(url(controller='files', action='index',
220 response = self.app.get(url(controller='files', action='index',
212 repo_name=GIT_REPO,
221 repo_name=GIT_REPO,
213 revision='master',
222 revision='master',
214 f_path='vcs/nodes.py',
223 f_path='vcs/nodes.py',
215 annotate=True))
224 annotate=True))
216
225
217 def test_archival(self):
226 def test_archival(self):
218 self.log_user()
227 self.log_user()
219
228 _set_downloads(HG_REPO, set_to=True)
220 for arch_ext, info in ARCHIVE_SPECS.items():
229 for arch_ext, info in ARCHIVE_SPECS.items():
221 short = '27cd5cce30c9%s' % arch_ext
230 short = '27cd5cce30c9%s' % arch_ext
222 fname = '27cd5cce30c96924232dffcd24178a07ffeb5dfc%s' % arch_ext
231 fname = '27cd5cce30c96924232dffcd24178a07ffeb5dfc%s' % arch_ext
223 filename = '%s-%s' % (HG_REPO, short)
232 filename = '%s-%s' % (HG_REPO, short)
224 response = self.app.get(url(controller='files',
233 response = self.app.get(url(controller='files',
225 action='archivefile',
234 action='archivefile',
226 repo_name=HG_REPO,
235 repo_name=HG_REPO,
227 fname=fname))
236 fname=fname))
228
237
229 self.assertEqual(response.status, '200 OK')
238 self.assertEqual(response.status, '200 OK')
230 heads = [
239 heads = [
231 ('Pragma', 'no-cache'),
240 ('Pragma', 'no-cache'),
232 ('Cache-Control', 'no-cache'),
241 ('Cache-Control', 'no-cache'),
233 ('Content-Disposition', 'attachment; filename=%s' % filename),
242 ('Content-Disposition', 'attachment; filename=%s' % filename),
234 ('Content-Type', '%s; charset=utf-8' % info[0]),
243 ('Content-Type', '%s; charset=utf-8' % info[0]),
235 ]
244 ]
236 self.assertEqual(response.response._headers.items(), heads)
245 self.assertEqual(response.response._headers.items(), heads)
237
246
238 def test_archival_wrong_ext(self):
247 def test_archival_wrong_ext(self):
239 self.log_user()
248 self.log_user()
240
249 _set_downloads(HG_REPO, set_to=True)
241 for arch_ext in ['tar', 'rar', 'x', '..ax', '.zipz']:
250 for arch_ext in ['tar', 'rar', 'x', '..ax', '.zipz']:
242 fname = '27cd5cce30c96924232dffcd24178a07ffeb5dfc%s' % arch_ext
251 fname = '27cd5cce30c96924232dffcd24178a07ffeb5dfc%s' % arch_ext
243
252
244 response = self.app.get(url(controller='files',
253 response = self.app.get(url(controller='files',
245 action='archivefile',
254 action='archivefile',
246 repo_name=HG_REPO,
255 repo_name=HG_REPO,
247 fname=fname))
256 fname=fname))
248 response.mustcontain('Unknown archive type')
257 response.mustcontain('Unknown archive type')
249
258
250 def test_archival_wrong_revision(self):
259 def test_archival_wrong_revision(self):
251 self.log_user()
260 self.log_user()
252
261 _set_downloads(HG_REPO, set_to=True)
253 for rev in ['00x000000', 'tar', 'wrong', '@##$@$42413232', '232dffcd']:
262 for rev in ['00x000000', 'tar', 'wrong', '@##$@$42413232', '232dffcd']:
254 fname = '%s.zip' % rev
263 fname = '%s.zip' % rev
255
264
256 response = self.app.get(url(controller='files',
265 response = self.app.get(url(controller='files',
257 action='archivefile',
266 action='archivefile',
258 repo_name=HG_REPO,
267 repo_name=HG_REPO,
259 fname=fname))
268 fname=fname))
260 response.mustcontain('Unknown revision')
269 response.mustcontain('Unknown revision')
261
270
262 #==========================================================================
271 #==========================================================================
263 # RAW FILE
272 # RAW FILE
264 #==========================================================================
273 #==========================================================================
265 def test_raw_file_ok(self):
274 def test_raw_file_ok(self):
266 self.log_user()
275 self.log_user()
267 response = self.app.get(url(controller='files', action='rawfile',
276 response = self.app.get(url(controller='files', action='rawfile',
268 repo_name=HG_REPO,
277 repo_name=HG_REPO,
269 revision='27cd5cce30c96924232dffcd24178a07ffeb5dfc',
278 revision='27cd5cce30c96924232dffcd24178a07ffeb5dfc',
270 f_path='vcs/nodes.py'))
279 f_path='vcs/nodes.py'))
271
280
272 self.assertEqual(response.content_disposition, "attachment; filename=nodes.py")
281 self.assertEqual(response.content_disposition, "attachment; filename=nodes.py")
273 self.assertEqual(response.content_type, "text/x-python")
282 self.assertEqual(response.content_type, "text/x-python")
274
283
275 def test_raw_file_wrong_cs(self):
284 def test_raw_file_wrong_cs(self):
276 self.log_user()
285 self.log_user()
277 rev = u'ERRORce30c96924232dffcd24178a07ffeb5dfc'
286 rev = u'ERRORce30c96924232dffcd24178a07ffeb5dfc'
278 f_path = 'vcs/nodes.py'
287 f_path = 'vcs/nodes.py'
279
288
280 response = self.app.get(url(controller='files', action='rawfile',
289 response = self.app.get(url(controller='files', action='rawfile',
281 repo_name=HG_REPO,
290 repo_name=HG_REPO,
282 revision=rev,
291 revision=rev,
283 f_path=f_path))
292 f_path=f_path))
284
293
285 msg = """Revision %r does not exist for this repository""" % (rev)
294 msg = """Revision %r does not exist for this repository""" % (rev)
286 self.checkSessionFlash(response, msg)
295 self.checkSessionFlash(response, msg)
287
296
288 msg = """%s""" % (HG_REPO)
297 msg = """%s""" % (HG_REPO)
289 self.checkSessionFlash(response, msg)
298 self.checkSessionFlash(response, msg)
290
299
291 def test_raw_file_wrong_f_path(self):
300 def test_raw_file_wrong_f_path(self):
292 self.log_user()
301 self.log_user()
293 rev = '27cd5cce30c96924232dffcd24178a07ffeb5dfc'
302 rev = '27cd5cce30c96924232dffcd24178a07ffeb5dfc'
294 f_path = 'vcs/ERRORnodes.py'
303 f_path = 'vcs/ERRORnodes.py'
295 response = self.app.get(url(controller='files', action='rawfile',
304 response = self.app.get(url(controller='files', action='rawfile',
296 repo_name=HG_REPO,
305 repo_name=HG_REPO,
297 revision=rev,
306 revision=rev,
298 f_path=f_path))
307 f_path=f_path))
299
308
300 msg = "There is no file nor directory at the given path: %r at revision %r" % (f_path, rev[:12])
309 msg = "There is no file nor directory at the given path: %r at revision %r" % (f_path, rev[:12])
301 self.checkSessionFlash(response, msg)
310 self.checkSessionFlash(response, msg)
302
311
303 #==========================================================================
312 #==========================================================================
304 # RAW RESPONSE - PLAIN
313 # RAW RESPONSE - PLAIN
305 #==========================================================================
314 #==========================================================================
306 def test_raw_ok(self):
315 def test_raw_ok(self):
307 self.log_user()
316 self.log_user()
308 response = self.app.get(url(controller='files', action='raw',
317 response = self.app.get(url(controller='files', action='raw',
309 repo_name=HG_REPO,
318 repo_name=HG_REPO,
310 revision='27cd5cce30c96924232dffcd24178a07ffeb5dfc',
319 revision='27cd5cce30c96924232dffcd24178a07ffeb5dfc',
311 f_path='vcs/nodes.py'))
320 f_path='vcs/nodes.py'))
312
321
313 self.assertEqual(response.content_type, "text/plain")
322 self.assertEqual(response.content_type, "text/plain")
314
323
315 def test_raw_wrong_cs(self):
324 def test_raw_wrong_cs(self):
316 self.log_user()
325 self.log_user()
317 rev = u'ERRORcce30c96924232dffcd24178a07ffeb5dfc'
326 rev = u'ERRORcce30c96924232dffcd24178a07ffeb5dfc'
318 f_path = 'vcs/nodes.py'
327 f_path = 'vcs/nodes.py'
319
328
320 response = self.app.get(url(controller='files', action='raw',
329 response = self.app.get(url(controller='files', action='raw',
321 repo_name=HG_REPO,
330 repo_name=HG_REPO,
322 revision=rev,
331 revision=rev,
323 f_path=f_path))
332 f_path=f_path))
324 msg = """Revision %r does not exist for this repository""" % (rev)
333 msg = """Revision %r does not exist for this repository""" % (rev)
325 self.checkSessionFlash(response, msg)
334 self.checkSessionFlash(response, msg)
326
335
327 msg = """%s""" % (HG_REPO)
336 msg = """%s""" % (HG_REPO)
328 self.checkSessionFlash(response, msg)
337 self.checkSessionFlash(response, msg)
329
338
330 def test_raw_wrong_f_path(self):
339 def test_raw_wrong_f_path(self):
331 self.log_user()
340 self.log_user()
332 rev = '27cd5cce30c96924232dffcd24178a07ffeb5dfc'
341 rev = '27cd5cce30c96924232dffcd24178a07ffeb5dfc'
333 f_path = 'vcs/ERRORnodes.py'
342 f_path = 'vcs/ERRORnodes.py'
334 response = self.app.get(url(controller='files', action='raw',
343 response = self.app.get(url(controller='files', action='raw',
335 repo_name=HG_REPO,
344 repo_name=HG_REPO,
336 revision=rev,
345 revision=rev,
337 f_path=f_path))
346 f_path=f_path))
338 msg = "There is no file nor directory at the given path: %r at revision %r" % (f_path, rev[:12])
347 msg = "There is no file nor directory at the given path: %r at revision %r" % (f_path, rev[:12])
339 self.checkSessionFlash(response, msg)
348 self.checkSessionFlash(response, msg)
340
349
341 def test_ajaxed_files_list(self):
350 def test_ajaxed_files_list(self):
342 self.log_user()
351 self.log_user()
343 rev = '27cd5cce30c96924232dffcd24178a07ffeb5dfc'
352 rev = '27cd5cce30c96924232dffcd24178a07ffeb5dfc'
344 response = self.app.get(
353 response = self.app.get(
345 url('files_nodelist_home', repo_name=HG_REPO,f_path='/',revision=rev),
354 url('files_nodelist_home', repo_name=HG_REPO,f_path='/',revision=rev),
346 extra_environ={'HTTP_X_PARTIAL_XHR': '1'},
355 extra_environ={'HTTP_X_PARTIAL_XHR': '1'},
347 )
356 )
348 response.mustcontain("vcs/web/simplevcs/views/repository.py")
357 response.mustcontain("vcs/web/simplevcs/views/repository.py")
@@ -1,175 +1,165 b''
1 import os
1 import os
2 import unittest
2 import unittest
3 from rhodecode.tests import *
3 from rhodecode.tests import *
4
4
5 from rhodecode.model.repos_group import ReposGroupModel
5 from rhodecode.model.repos_group import ReposGroupModel
6 from rhodecode.model.repo import RepoModel
6 from rhodecode.model.repo import RepoModel
7 from rhodecode.model.db import RepoGroup, User, Repository
7 from rhodecode.model.db import RepoGroup, User
8 from rhodecode.model.meta import Session
8 from rhodecode.model.meta import Session
9 from sqlalchemy.exc import IntegrityError
9 from sqlalchemy.exc import IntegrityError
10
10
11
11
12 def _make_group(path, desc='desc', parent_id=None,
12 def _make_group(path, desc='desc', parent_id=None,
13 skip_if_exists=False):
13 skip_if_exists=False):
14
14
15 gr = RepoGroup.get_by_group_name(path)
15 gr = RepoGroup.get_by_group_name(path)
16 if gr and skip_if_exists:
16 if gr and skip_if_exists:
17 return gr
17 return gr
18 if isinstance(parent_id, RepoGroup):
18 if isinstance(parent_id, RepoGroup):
19 parent_id = parent_id.group_id
19 parent_id = parent_id.group_id
20 gr = ReposGroupModel().create(path, desc, parent_id)
20 gr = ReposGroupModel().create(path, desc, parent_id)
21 return gr
21 return gr
22
22
23
23
24 class TestReposGroups(unittest.TestCase):
24 class TestReposGroups(unittest.TestCase):
25
25
26 def setUp(self):
26 def setUp(self):
27 self.g1 = _make_group('test1', skip_if_exists=True)
27 self.g1 = _make_group('test1', skip_if_exists=True)
28 Session().commit()
28 Session().commit()
29 self.g2 = _make_group('test2', skip_if_exists=True)
29 self.g2 = _make_group('test2', skip_if_exists=True)
30 Session().commit()
30 Session().commit()
31 self.g3 = _make_group('test3', skip_if_exists=True)
31 self.g3 = _make_group('test3', skip_if_exists=True)
32 Session().commit()
32 Session().commit()
33
33
34 def tearDown(self):
34 def tearDown(self):
35 print 'out'
35 print 'out'
36
36
37 def __check_path(self, *path):
37 def __check_path(self, *path):
38 """
38 """
39 Checks the path for existance !
39 Checks the path for existance !
40 """
40 """
41 path = [TESTS_TMP_PATH] + list(path)
41 path = [TESTS_TMP_PATH] + list(path)
42 path = os.path.join(*path)
42 path = os.path.join(*path)
43 return os.path.isdir(path)
43 return os.path.isdir(path)
44
44
45 def _check_folders(self):
45 def _check_folders(self):
46 print os.listdir(TESTS_TMP_PATH)
46 print os.listdir(TESTS_TMP_PATH)
47
47
48 def __delete_group(self, id_):
48 def __delete_group(self, id_):
49 ReposGroupModel().delete(id_)
49 ReposGroupModel().delete(id_)
50
50
51 def __update_group(self, id_, path, desc='desc', parent_id=None):
51 def __update_group(self, id_, path, desc='desc', parent_id=None):
52 form_data = dict(
52 form_data = dict(
53 group_name=path,
53 group_name=path,
54 group_description=desc,
54 group_description=desc,
55 group_parent_id=parent_id,
55 group_parent_id=parent_id,
56 perms_updates=[],
56 perms_updates=[],
57 perms_new=[],
57 perms_new=[],
58 enable_locking=False,
58 enable_locking=False,
59 recursive=False
59 recursive=False
60 )
60 )
61 gr = ReposGroupModel().update(id_, form_data)
61 gr = ReposGroupModel().update(id_, form_data)
62 return gr
62 return gr
63
63
64 def test_create_group(self):
64 def test_create_group(self):
65 g = _make_group('newGroup')
65 g = _make_group('newGroup')
66 self.assertEqual(g.full_path, 'newGroup')
66 self.assertEqual(g.full_path, 'newGroup')
67
67
68 self.assertTrue(self.__check_path('newGroup'))
68 self.assertTrue(self.__check_path('newGroup'))
69
69
70 def test_create_same_name_group(self):
70 def test_create_same_name_group(self):
71 self.assertRaises(IntegrityError, lambda: _make_group('newGroup'))
71 self.assertRaises(IntegrityError, lambda: _make_group('newGroup'))
72 Session().rollback()
72 Session().rollback()
73
73
74 def test_same_subgroup(self):
74 def test_same_subgroup(self):
75 sg1 = _make_group('sub1', parent_id=self.g1.group_id)
75 sg1 = _make_group('sub1', parent_id=self.g1.group_id)
76 self.assertEqual(sg1.parent_group, self.g1)
76 self.assertEqual(sg1.parent_group, self.g1)
77 self.assertEqual(sg1.full_path, 'test1/sub1')
77 self.assertEqual(sg1.full_path, 'test1/sub1')
78 self.assertTrue(self.__check_path('test1', 'sub1'))
78 self.assertTrue(self.__check_path('test1', 'sub1'))
79
79
80 ssg1 = _make_group('subsub1', parent_id=sg1.group_id)
80 ssg1 = _make_group('subsub1', parent_id=sg1.group_id)
81 self.assertEqual(ssg1.parent_group, sg1)
81 self.assertEqual(ssg1.parent_group, sg1)
82 self.assertEqual(ssg1.full_path, 'test1/sub1/subsub1')
82 self.assertEqual(ssg1.full_path, 'test1/sub1/subsub1')
83 self.assertTrue(self.__check_path('test1', 'sub1', 'subsub1'))
83 self.assertTrue(self.__check_path('test1', 'sub1', 'subsub1'))
84
84
85 def test_remove_group(self):
85 def test_remove_group(self):
86 sg1 = _make_group('deleteme')
86 sg1 = _make_group('deleteme')
87 self.__delete_group(sg1.group_id)
87 self.__delete_group(sg1.group_id)
88
88
89 self.assertEqual(RepoGroup.get(sg1.group_id), None)
89 self.assertEqual(RepoGroup.get(sg1.group_id), None)
90 self.assertFalse(self.__check_path('deteteme'))
90 self.assertFalse(self.__check_path('deteteme'))
91
91
92 sg1 = _make_group('deleteme', parent_id=self.g1.group_id)
92 sg1 = _make_group('deleteme', parent_id=self.g1.group_id)
93 self.__delete_group(sg1.group_id)
93 self.__delete_group(sg1.group_id)
94
94
95 self.assertEqual(RepoGroup.get(sg1.group_id), None)
95 self.assertEqual(RepoGroup.get(sg1.group_id), None)
96 self.assertFalse(self.__check_path('test1', 'deteteme'))
96 self.assertFalse(self.__check_path('test1', 'deteteme'))
97
97
98 def test_rename_single_group(self):
98 def test_rename_single_group(self):
99 sg1 = _make_group('initial')
99 sg1 = _make_group('initial')
100
100
101 new_sg1 = self.__update_group(sg1.group_id, 'after')
101 new_sg1 = self.__update_group(sg1.group_id, 'after')
102 self.assertTrue(self.__check_path('after'))
102 self.assertTrue(self.__check_path('after'))
103 self.assertEqual(RepoGroup.get_by_group_name('initial'), None)
103 self.assertEqual(RepoGroup.get_by_group_name('initial'), None)
104
104
105 def test_update_group_parent(self):
105 def test_update_group_parent(self):
106
106
107 sg1 = _make_group('initial', parent_id=self.g1.group_id)
107 sg1 = _make_group('initial', parent_id=self.g1.group_id)
108
108
109 new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g1.group_id)
109 new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g1.group_id)
110 self.assertTrue(self.__check_path('test1', 'after'))
110 self.assertTrue(self.__check_path('test1', 'after'))
111 self.assertEqual(RepoGroup.get_by_group_name('test1/initial'), None)
111 self.assertEqual(RepoGroup.get_by_group_name('test1/initial'), None)
112
112
113 new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g3.group_id)
113 new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g3.group_id)
114 self.assertTrue(self.__check_path('test3', 'after'))
114 self.assertTrue(self.__check_path('test3', 'after'))
115 self.assertEqual(RepoGroup.get_by_group_name('test3/initial'), None)
115 self.assertEqual(RepoGroup.get_by_group_name('test3/initial'), None)
116
116
117 new_sg1 = self.__update_group(sg1.group_id, 'hello')
117 new_sg1 = self.__update_group(sg1.group_id, 'hello')
118 self.assertTrue(self.__check_path('hello'))
118 self.assertTrue(self.__check_path('hello'))
119
119
120 self.assertEqual(RepoGroup.get_by_group_name('hello'), new_sg1)
120 self.assertEqual(RepoGroup.get_by_group_name('hello'), new_sg1)
121
121
122 def test_subgrouping_with_repo(self):
122 def test_subgrouping_with_repo(self):
123
123
124 g1 = _make_group('g1')
124 g1 = _make_group('g1')
125 g2 = _make_group('g2')
125 g2 = _make_group('g2')
126
126
127 # create new repo
127 # create new repo
128 form_data = dict(repo_name='john',
128 form_data = _get_repo_create_params(repo_name='john')
129 repo_name_full='john',
130 fork_name=None,
131 description=None,
132 repo_group=None,
133 private=False,
134 repo_type='hg',
135 clone_uri=None,
136 landing_rev='tip',
137 enable_locking=False,
138 recursive=False)
139 cur_user = User.get_by_username(TEST_USER_ADMIN_LOGIN)
129 cur_user = User.get_by_username(TEST_USER_ADMIN_LOGIN)
140 r = RepoModel().create(form_data, cur_user)
130 r = RepoModel().create(form_data, cur_user)
141
131
142 self.assertEqual(r.repo_name, 'john')
132 self.assertEqual(r.repo_name, 'john')
143
133
144 # put repo into group
134 # put repo into group
145 form_data = form_data
135 form_data = form_data
146 form_data['repo_group'] = g1.group_id
136 form_data['repo_group'] = g1.group_id
147 form_data['perms_new'] = []
137 form_data['perms_new'] = []
148 form_data['perms_updates'] = []
138 form_data['perms_updates'] = []
149 RepoModel().update(r.repo_name, form_data)
139 RepoModel().update(r.repo_name, form_data)
150 self.assertEqual(r.repo_name, 'g1/john')
140 self.assertEqual(r.repo_name, 'g1/john')
151
141
152 self.__update_group(g1.group_id, 'g1', parent_id=g2.group_id)
142 self.__update_group(g1.group_id, 'g1', parent_id=g2.group_id)
153 self.assertTrue(self.__check_path('g2', 'g1'))
143 self.assertTrue(self.__check_path('g2', 'g1'))
154
144
155 # test repo
145 # test repo
156 self.assertEqual(r.repo_name, RepoGroup.url_sep().join(['g2', 'g1',
146 self.assertEqual(r.repo_name, RepoGroup.url_sep().join(['g2', 'g1',
157 r.just_name]))
147 r.just_name]))
158
148
159 def test_move_to_root(self):
149 def test_move_to_root(self):
160 g1 = _make_group('t11')
150 g1 = _make_group('t11')
161 Session().commit()
151 Session().commit()
162 g2 = _make_group('t22', parent_id=g1.group_id)
152 g2 = _make_group('t22', parent_id=g1.group_id)
163 Session().commit()
153 Session().commit()
164
154
165 self.assertEqual(g2.full_path, 't11/t22')
155 self.assertEqual(g2.full_path, 't11/t22')
166 self.assertTrue(self.__check_path('t11', 't22'))
156 self.assertTrue(self.__check_path('t11', 't22'))
167
157
168 g2 = self.__update_group(g2.group_id, 'g22', parent_id=None)
158 g2 = self.__update_group(g2.group_id, 'g22', parent_id=None)
169 Session().commit()
159 Session().commit()
170
160
171 self.assertEqual(g2.group_name, 'g22')
161 self.assertEqual(g2.group_name, 'g22')
172 # we moved out group from t1 to '' so it's full path should be 'g2'
162 # we moved out group from t1 to '' so it's full path should be 'g2'
173 self.assertEqual(g2.full_path, 'g22')
163 self.assertEqual(g2.full_path, 'g22')
174 self.assertFalse(self.__check_path('t11', 't22'))
164 self.assertFalse(self.__check_path('t11', 't22'))
175 self.assertTrue(self.__check_path('g22'))
165 self.assertTrue(self.__check_path('g22'))
General Comments 0
You need to be logged in to leave comments. Login now