##// END OF EJS Templates
removed obsolete admin_user tmpl context variables
marcink -
r3712:08cf7741 beta
parent child Browse files
Show More
@@ -1,150 +1,148 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.ldap_settings
3 rhodecode.controllers.admin.ldap_settings
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 ldap controller for RhodeCode
6 ldap controller for RhodeCode
7
7
8 :created_on: Nov 26, 2010
8 :created_on: Nov 26, 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 import logging
25 import logging
26 import formencode
26 import formencode
27 import traceback
27 import traceback
28
28
29 from formencode import htmlfill
29 from formencode import htmlfill
30
30
31 from pylons import request, response, session, tmpl_context as c, url
31 from pylons import request, response, session, tmpl_context as c, url
32 from pylons.controllers.util import abort, redirect
32 from pylons.controllers.util import abort, redirect
33 from pylons.i18n.translation import _
33 from pylons.i18n.translation import _
34
34
35 from sqlalchemy.exc import DatabaseError
35 from sqlalchemy.exc import DatabaseError
36
36
37 from rhodecode.lib.base import BaseController, render
37 from rhodecode.lib.base import BaseController, render
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 from rhodecode.lib.exceptions import LdapImportError
40 from rhodecode.lib.exceptions import LdapImportError
41 from rhodecode.model.forms import LdapSettingsForm
41 from rhodecode.model.forms import LdapSettingsForm
42 from rhodecode.model.db import RhodeCodeSetting
42 from rhodecode.model.db import RhodeCodeSetting
43 from rhodecode.model.meta import Session
43 from rhodecode.model.meta import Session
44
44
45 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
46
46
47
47
48 class LdapSettingsController(BaseController):
48 class LdapSettingsController(BaseController):
49
49
50 search_scope_choices = [('BASE', _('BASE'),),
50 search_scope_choices = [('BASE', _('BASE'),),
51 ('ONELEVEL', _('ONELEVEL'),),
51 ('ONELEVEL', _('ONELEVEL'),),
52 ('SUBTREE', _('SUBTREE'),),
52 ('SUBTREE', _('SUBTREE'),),
53 ]
53 ]
54 search_scope_default = 'SUBTREE'
54 search_scope_default = 'SUBTREE'
55
55
56 tls_reqcert_choices = [('NEVER', _('NEVER'),),
56 tls_reqcert_choices = [('NEVER', _('NEVER'),),
57 ('ALLOW', _('ALLOW'),),
57 ('ALLOW', _('ALLOW'),),
58 ('TRY', _('TRY'),),
58 ('TRY', _('TRY'),),
59 ('DEMAND', _('DEMAND'),),
59 ('DEMAND', _('DEMAND'),),
60 ('HARD', _('HARD'),),
60 ('HARD', _('HARD'),),
61 ]
61 ]
62 tls_reqcert_default = 'DEMAND'
62 tls_reqcert_default = 'DEMAND'
63
63
64 tls_kind_choices = [('PLAIN', _('No encryption'),),
64 tls_kind_choices = [('PLAIN', _('No encryption'),),
65 ('LDAPS', _('LDAPS connection'),),
65 ('LDAPS', _('LDAPS connection'),),
66 ('START_TLS', _('START_TLS on LDAP connection'),)
66 ('START_TLS', _('START_TLS on LDAP connection'),)
67 ]
67 ]
68
68
69 tls_kind_default = 'PLAIN'
69 tls_kind_default = 'PLAIN'
70
70
71 @LoginRequired()
71 @LoginRequired()
72 @HasPermissionAllDecorator('hg.admin')
72 @HasPermissionAllDecorator('hg.admin')
73 def __before__(self):
73 def __before__(self):
74 c.admin_user = session.get('admin_user')
75 c.admin_username = session.get('admin_username')
76 c.search_scope_choices = self.search_scope_choices
74 c.search_scope_choices = self.search_scope_choices
77 c.tls_reqcert_choices = self.tls_reqcert_choices
75 c.tls_reqcert_choices = self.tls_reqcert_choices
78 c.tls_kind_choices = self.tls_kind_choices
76 c.tls_kind_choices = self.tls_kind_choices
79
77
80 c.search_scope_cur = self.search_scope_default
78 c.search_scope_cur = self.search_scope_default
81 c.tls_reqcert_cur = self.tls_reqcert_default
79 c.tls_reqcert_cur = self.tls_reqcert_default
82 c.tls_kind_cur = self.tls_kind_default
80 c.tls_kind_cur = self.tls_kind_default
83
81
84 super(LdapSettingsController, self).__before__()
82 super(LdapSettingsController, self).__before__()
85
83
86 def index(self):
84 def index(self):
87 defaults = RhodeCodeSetting.get_ldap_settings()
85 defaults = RhodeCodeSetting.get_ldap_settings()
88 c.search_scope_cur = defaults.get('ldap_search_scope')
86 c.search_scope_cur = defaults.get('ldap_search_scope')
89 c.tls_reqcert_cur = defaults.get('ldap_tls_reqcert')
87 c.tls_reqcert_cur = defaults.get('ldap_tls_reqcert')
90 c.tls_kind_cur = defaults.get('ldap_tls_kind')
88 c.tls_kind_cur = defaults.get('ldap_tls_kind')
91
89
92 return htmlfill.render(
90 return htmlfill.render(
93 render('admin/ldap/ldap.html'),
91 render('admin/ldap/ldap.html'),
94 defaults=defaults,
92 defaults=defaults,
95 encoding="UTF-8",
93 encoding="UTF-8",
96 force_defaults=True,)
94 force_defaults=True,)
97
95
98 def ldap_settings(self):
96 def ldap_settings(self):
99 """POST ldap create and store ldap settings"""
97 """POST ldap create and store ldap settings"""
100
98
101 _form = LdapSettingsForm([x[0] for x in self.tls_reqcert_choices],
99 _form = LdapSettingsForm([x[0] for x in self.tls_reqcert_choices],
102 [x[0] for x in self.search_scope_choices],
100 [x[0] for x in self.search_scope_choices],
103 [x[0] for x in self.tls_kind_choices])()
101 [x[0] for x in self.tls_kind_choices])()
104 # check the ldap lib
102 # check the ldap lib
105 ldap_active = False
103 ldap_active = False
106 try:
104 try:
107 import ldap
105 import ldap
108 ldap_active = True
106 ldap_active = True
109 except ImportError:
107 except ImportError:
110 pass
108 pass
111
109
112 try:
110 try:
113 form_result = _form.to_python(dict(request.POST))
111 form_result = _form.to_python(dict(request.POST))
114
112
115 try:
113 try:
116
114
117 for k, v in form_result.items():
115 for k, v in form_result.items():
118 if k.startswith('ldap_'):
116 if k.startswith('ldap_'):
119 if k == 'ldap_active':
117 if k == 'ldap_active':
120 v = ldap_active
118 v = ldap_active
121 setting = RhodeCodeSetting.get_by_name(k)
119 setting = RhodeCodeSetting.get_by_name(k)
122 setting.app_settings_value = v
120 setting.app_settings_value = v
123 Session().add(setting)
121 Session().add(setting)
124
122
125 Session().commit()
123 Session().commit()
126 h.flash(_('LDAP settings updated successfully'),
124 h.flash(_('LDAP settings updated successfully'),
127 category='success')
125 category='success')
128 if not ldap_active:
126 if not ldap_active:
129 #if ldap is missing send an info to user
127 #if ldap is missing send an info to user
130 h.flash(_('Unable to activate ldap. The "python-ldap" library '
128 h.flash(_('Unable to activate ldap. The "python-ldap" library '
131 'is missing.'), category='warning')
129 'is missing.'), category='warning')
132
130
133 except (DatabaseError,):
131 except (DatabaseError,):
134 raise
132 raise
135
133
136 except formencode.Invalid, errors:
134 except formencode.Invalid, errors:
137 e = errors.error_dict or {}
135 e = errors.error_dict or {}
138
136
139 return htmlfill.render(
137 return htmlfill.render(
140 render('admin/ldap/ldap.html'),
138 render('admin/ldap/ldap.html'),
141 defaults=errors.value,
139 defaults=errors.value,
142 errors=e,
140 errors=e,
143 prefix_error=False,
141 prefix_error=False,
144 encoding="UTF-8")
142 encoding="UTF-8")
145 except Exception:
143 except Exception:
146 log.error(traceback.format_exc())
144 log.error(traceback.format_exc())
147 h.flash(_('Error occurred during update of ldap settings'),
145 h.flash(_('Error occurred during update of ldap settings'),
148 category='error')
146 category='error')
149
147
150 return redirect(url('ldap_home'))
148 return redirect(url('ldap_home'))
@@ -1,194 +1,192 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.permissions
3 rhodecode.controllers.admin.permissions
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 permissions controller for Rhodecode
6 permissions controller for Rhodecode
7
7
8 :created_on: Apr 27, 2010
8 :created_on: Apr 27, 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 pylons import request, session, tmpl_context as c, url
31 from pylons import request, session, tmpl_context as c, url
32 from pylons.controllers.util import abort, redirect
32 from pylons.controllers.util import abort, redirect
33 from pylons.i18n.translation import _
33 from pylons.i18n.translation import _
34
34
35 from rhodecode.lib import helpers as h
35 from rhodecode.lib import helpers as h
36 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator,\
36 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator,\
37 AuthUser
37 AuthUser
38 from rhodecode.lib.base import BaseController, render
38 from rhodecode.lib.base import BaseController, render
39 from rhodecode.model.forms import DefaultPermissionsForm
39 from rhodecode.model.forms import DefaultPermissionsForm
40 from rhodecode.model.permission import PermissionModel
40 from rhodecode.model.permission import PermissionModel
41 from rhodecode.model.db import User, UserIpMap
41 from rhodecode.model.db import User, UserIpMap
42 from rhodecode.model.meta import Session
42 from rhodecode.model.meta import Session
43
43
44 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
45
45
46
46
47 class PermissionsController(BaseController):
47 class PermissionsController(BaseController):
48 """REST Controller styled on the Atom Publishing Protocol"""
48 """REST Controller styled on the Atom Publishing Protocol"""
49 # To properly map this controller, ensure your config/routing.py
49 # To properly map this controller, ensure your config/routing.py
50 # file has a resource setup:
50 # file has a resource setup:
51 # map.resource('permission', 'permissions')
51 # map.resource('permission', 'permissions')
52
52
53 @LoginRequired()
53 @LoginRequired()
54 @HasPermissionAllDecorator('hg.admin')
54 @HasPermissionAllDecorator('hg.admin')
55 def __before__(self):
55 def __before__(self):
56 c.admin_user = session.get('admin_user')
57 c.admin_username = session.get('admin_username')
58 super(PermissionsController, self).__before__()
56 super(PermissionsController, self).__before__()
59
57
60 self.repo_perms_choices = [('repository.none', _('None'),),
58 self.repo_perms_choices = [('repository.none', _('None'),),
61 ('repository.read', _('Read'),),
59 ('repository.read', _('Read'),),
62 ('repository.write', _('Write'),),
60 ('repository.write', _('Write'),),
63 ('repository.admin', _('Admin'),)]
61 ('repository.admin', _('Admin'),)]
64 self.group_perms_choices = [('group.none', _('None'),),
62 self.group_perms_choices = [('group.none', _('None'),),
65 ('group.read', _('Read'),),
63 ('group.read', _('Read'),),
66 ('group.write', _('Write'),),
64 ('group.write', _('Write'),),
67 ('group.admin', _('Admin'),)]
65 ('group.admin', _('Admin'),)]
68 self.register_choices = [
66 self.register_choices = [
69 ('hg.register.none',
67 ('hg.register.none',
70 _('Disabled')),
68 _('Disabled')),
71 ('hg.register.manual_activate',
69 ('hg.register.manual_activate',
72 _('Allowed with manual account activation')),
70 _('Allowed with manual account activation')),
73 ('hg.register.auto_activate',
71 ('hg.register.auto_activate',
74 _('Allowed with automatic account activation')), ]
72 _('Allowed with automatic account activation')), ]
75
73
76 self.create_choices = [('hg.create.none', _('Disabled')),
74 self.create_choices = [('hg.create.none', _('Disabled')),
77 ('hg.create.repository', _('Enabled'))]
75 ('hg.create.repository', _('Enabled'))]
78
76
79 self.fork_choices = [('hg.fork.none', _('Disabled')),
77 self.fork_choices = [('hg.fork.none', _('Disabled')),
80 ('hg.fork.repository', _('Enabled'))]
78 ('hg.fork.repository', _('Enabled'))]
81
79
82 # set the global template variables
80 # set the global template variables
83 c.repo_perms_choices = self.repo_perms_choices
81 c.repo_perms_choices = self.repo_perms_choices
84 c.group_perms_choices = self.group_perms_choices
82 c.group_perms_choices = self.group_perms_choices
85 c.register_choices = self.register_choices
83 c.register_choices = self.register_choices
86 c.create_choices = self.create_choices
84 c.create_choices = self.create_choices
87 c.fork_choices = self.fork_choices
85 c.fork_choices = self.fork_choices
88
86
89 def index(self, format='html'):
87 def index(self, format='html'):
90 """GET /permissions: All items in the collection"""
88 """GET /permissions: All items in the collection"""
91 # url('permissions')
89 # url('permissions')
92
90
93 def create(self):
91 def create(self):
94 """POST /permissions: Create a new item"""
92 """POST /permissions: Create a new item"""
95 # url('permissions')
93 # url('permissions')
96
94
97 def new(self, format='html'):
95 def new(self, format='html'):
98 """GET /permissions/new: Form to create a new item"""
96 """GET /permissions/new: Form to create a new item"""
99 # url('new_permission')
97 # url('new_permission')
100
98
101 def update(self, id):
99 def update(self, id):
102 """PUT /permissions/id: Update an existing item"""
100 """PUT /permissions/id: Update an existing item"""
103 # Forms posted to this method should contain a hidden field:
101 # Forms posted to this method should contain a hidden field:
104 # <input type="hidden" name="_method" value="PUT" />
102 # <input type="hidden" name="_method" value="PUT" />
105 # Or using helpers:
103 # Or using helpers:
106 # h.form(url('permission', id=ID),
104 # h.form(url('permission', id=ID),
107 # method='put')
105 # method='put')
108 # url('permission', id=ID)
106 # url('permission', id=ID)
109 if id == 'default':
107 if id == 'default':
110 c.user = default_user = User.get_by_username('default')
108 c.user = default_user = User.get_by_username('default')
111 c.perm_user = AuthUser(user_id=default_user.user_id)
109 c.perm_user = AuthUser(user_id=default_user.user_id)
112 c.user_ip_map = UserIpMap.query()\
110 c.user_ip_map = UserIpMap.query()\
113 .filter(UserIpMap.user == default_user).all()
111 .filter(UserIpMap.user == default_user).all()
114 permission_model = PermissionModel()
112 permission_model = PermissionModel()
115
113
116 _form = DefaultPermissionsForm(
114 _form = DefaultPermissionsForm(
117 [x[0] for x in self.repo_perms_choices],
115 [x[0] for x in self.repo_perms_choices],
118 [x[0] for x in self.group_perms_choices],
116 [x[0] for x in self.group_perms_choices],
119 [x[0] for x in self.register_choices],
117 [x[0] for x in self.register_choices],
120 [x[0] for x in self.create_choices],
118 [x[0] for x in self.create_choices],
121 [x[0] for x in self.fork_choices])()
119 [x[0] for x in self.fork_choices])()
122
120
123 try:
121 try:
124 form_result = _form.to_python(dict(request.POST))
122 form_result = _form.to_python(dict(request.POST))
125 form_result.update({'perm_user_name': id})
123 form_result.update({'perm_user_name': id})
126 permission_model.update(form_result)
124 permission_model.update(form_result)
127 Session().commit()
125 Session().commit()
128 h.flash(_('Default permissions updated successfully'),
126 h.flash(_('Default permissions updated successfully'),
129 category='success')
127 category='success')
130
128
131 except formencode.Invalid, errors:
129 except formencode.Invalid, errors:
132 defaults = errors.value
130 defaults = errors.value
133
131
134 return htmlfill.render(
132 return htmlfill.render(
135 render('admin/permissions/permissions.html'),
133 render('admin/permissions/permissions.html'),
136 defaults=defaults,
134 defaults=defaults,
137 errors=errors.error_dict or {},
135 errors=errors.error_dict or {},
138 prefix_error=False,
136 prefix_error=False,
139 encoding="UTF-8")
137 encoding="UTF-8")
140 except Exception:
138 except Exception:
141 log.error(traceback.format_exc())
139 log.error(traceback.format_exc())
142 h.flash(_('Error occurred during update of permissions'),
140 h.flash(_('Error occurred during update of permissions'),
143 category='error')
141 category='error')
144
142
145 return redirect(url('edit_permission', id=id))
143 return redirect(url('edit_permission', id=id))
146
144
147 def delete(self, id):
145 def delete(self, id):
148 """DELETE /permissions/id: Delete an existing item"""
146 """DELETE /permissions/id: Delete an existing item"""
149 # Forms posted to this method should contain a hidden field:
147 # Forms posted to this method should contain a hidden field:
150 # <input type="hidden" name="_method" value="DELETE" />
148 # <input type="hidden" name="_method" value="DELETE" />
151 # Or using helpers:
149 # Or using helpers:
152 # h.form(url('permission', id=ID),
150 # h.form(url('permission', id=ID),
153 # method='delete')
151 # method='delete')
154 # url('permission', id=ID)
152 # url('permission', id=ID)
155
153
156 def show(self, id, format='html'):
154 def show(self, id, format='html'):
157 """GET /permissions/id: Show a specific item"""
155 """GET /permissions/id: Show a specific item"""
158 # url('permission', id=ID)
156 # url('permission', id=ID)
159
157
160 def edit(self, id, format='html'):
158 def edit(self, id, format='html'):
161 """GET /permissions/id/edit: Form to edit an existing item"""
159 """GET /permissions/id/edit: Form to edit an existing item"""
162 #url('edit_permission', id=ID)
160 #url('edit_permission', id=ID)
163
161
164 #this form can only edit default user permissions
162 #this form can only edit default user permissions
165 if id == 'default':
163 if id == 'default':
166 c.user = default_user = User.get_by_username('default')
164 c.user = default_user = User.get_by_username('default')
167 defaults = {'anonymous': default_user.active}
165 defaults = {'anonymous': default_user.active}
168 c.perm_user = AuthUser(user_id=default_user.user_id)
166 c.perm_user = AuthUser(user_id=default_user.user_id)
169 c.user_ip_map = UserIpMap.query()\
167 c.user_ip_map = UserIpMap.query()\
170 .filter(UserIpMap.user == default_user).all()
168 .filter(UserIpMap.user == default_user).all()
171 for p in default_user.user_perms:
169 for p in default_user.user_perms:
172 if p.permission.permission_name.startswith('repository.'):
170 if p.permission.permission_name.startswith('repository.'):
173 defaults['default_repo_perm'] = p.permission.permission_name
171 defaults['default_repo_perm'] = p.permission.permission_name
174
172
175 if p.permission.permission_name.startswith('group.'):
173 if p.permission.permission_name.startswith('group.'):
176 defaults['default_group_perm'] = p.permission.permission_name
174 defaults['default_group_perm'] = p.permission.permission_name
177
175
178 if p.permission.permission_name.startswith('hg.register.'):
176 if p.permission.permission_name.startswith('hg.register.'):
179 defaults['default_register'] = p.permission.permission_name
177 defaults['default_register'] = p.permission.permission_name
180
178
181 if p.permission.permission_name.startswith('hg.create.'):
179 if p.permission.permission_name.startswith('hg.create.'):
182 defaults['default_create'] = p.permission.permission_name
180 defaults['default_create'] = p.permission.permission_name
183
181
184 if p.permission.permission_name.startswith('hg.fork.'):
182 if p.permission.permission_name.startswith('hg.fork.'):
185 defaults['default_fork'] = p.permission.permission_name
183 defaults['default_fork'] = p.permission.permission_name
186
184
187 return htmlfill.render(
185 return htmlfill.render(
188 render('admin/permissions/permissions.html'),
186 render('admin/permissions/permissions.html'),
189 defaults=defaults,
187 defaults=defaults,
190 encoding="UTF-8",
188 encoding="UTF-8",
191 force_defaults=False
189 force_defaults=False
192 )
190 )
193 else:
191 else:
194 return redirect(url('admin_home'))
192 return redirect(url('admin_home'))
@@ -1,606 +1,604 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, HTTPForbidden
31 from webob.exc import HTTPInternalServerError, HTTPForbidden
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, NotAnonymous,\
40 HasPermissionAnyDecorator, HasRepoPermissionAllDecorator, NotAnonymous,\
41 HasPermissionAny, HasReposGroupPermissionAny, HasRepoPermissionAnyDecorator
41 HasPermissionAny, HasReposGroupPermissionAny, HasRepoPermissionAnyDecorator
42 from rhodecode.lib.base import BaseRepoController, render
42 from rhodecode.lib.base import BaseRepoController, render
43 from rhodecode.lib.utils import action_logger, repo_name_slug
43 from rhodecode.lib.utils import action_logger, repo_name_slug
44 from rhodecode.lib.helpers import get_token
44 from rhodecode.lib.helpers import get_token
45 from rhodecode.model.meta import Session
45 from rhodecode.model.meta import Session
46 from rhodecode.model.db import User, Repository, UserFollowing, RepoGroup,\
46 from rhodecode.model.db import User, Repository, UserFollowing, RepoGroup,\
47 RhodeCodeSetting, RepositoryField
47 RhodeCodeSetting, RepositoryField
48 from rhodecode.model.forms import RepoForm, RepoFieldForm, RepoPermsForm
48 from rhodecode.model.forms import RepoForm, RepoFieldForm, RepoPermsForm
49 from rhodecode.model.scm import ScmModel, GroupList
49 from rhodecode.model.scm import ScmModel, GroupList
50 from rhodecode.model.repo import RepoModel
50 from rhodecode.model.repo import RepoModel
51 from rhodecode.lib.compat import json
51 from rhodecode.lib.compat import json
52 from sqlalchemy.sql.expression import func
52 from sqlalchemy.sql.expression import func
53 from rhodecode.lib.exceptions import AttachedForksError
53 from rhodecode.lib.exceptions import AttachedForksError
54
54
55 log = logging.getLogger(__name__)
55 log = logging.getLogger(__name__)
56
56
57
57
58 class ReposController(BaseRepoController):
58 class ReposController(BaseRepoController):
59 """
59 """
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('repo', 'repos')
63 # map.resource('repo', 'repos')
64
64
65 @LoginRequired()
65 @LoginRequired()
66 def __before__(self):
66 def __before__(self):
67 c.admin_user = session.get('admin_user')
68 c.admin_username = session.get('admin_username')
69 super(ReposController, self).__before__()
67 super(ReposController, self).__before__()
70
68
71 def __load_defaults(self):
69 def __load_defaults(self):
72 acl_groups = GroupList(RepoGroup.query().all(),
70 acl_groups = GroupList(RepoGroup.query().all(),
73 perm_set=['group.write', 'group.admin'])
71 perm_set=['group.write', 'group.admin'])
74 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
72 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
75 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
73 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
76
74
77 repo_model = RepoModel()
75 repo_model = RepoModel()
78 c.users_array = repo_model.get_users_js()
76 c.users_array = repo_model.get_users_js()
79 c.users_groups_array = repo_model.get_users_groups_js()
77 c.users_groups_array = repo_model.get_users_groups_js()
80 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
78 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
81 c.landing_revs_choices = choices
79 c.landing_revs_choices = choices
82
80
83 def __load_data(self, repo_name=None):
81 def __load_data(self, repo_name=None):
84 """
82 """
85 Load defaults settings for edit, and update
83 Load defaults settings for edit, and update
86
84
87 :param repo_name:
85 :param repo_name:
88 """
86 """
89 self.__load_defaults()
87 self.__load_defaults()
90
88
91 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
89 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
92 repo = db_repo.scm_instance
90 repo = db_repo.scm_instance
93
91
94 if c.repo_info is None:
92 if c.repo_info is None:
95 h.not_mapped_error(repo_name)
93 h.not_mapped_error(repo_name)
96 return redirect(url('repos'))
94 return redirect(url('repos'))
97
95
98 ##override defaults for exact repo info here git/hg etc
96 ##override defaults for exact repo info here git/hg etc
99 choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info)
97 choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info)
100 c.landing_revs_choices = choices
98 c.landing_revs_choices = choices
101
99
102 c.default_user_id = User.get_by_username('default').user_id
100 c.default_user_id = User.get_by_username('default').user_id
103 c.in_public_journal = UserFollowing.query()\
101 c.in_public_journal = UserFollowing.query()\
104 .filter(UserFollowing.user_id == c.default_user_id)\
102 .filter(UserFollowing.user_id == c.default_user_id)\
105 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
103 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
106
104
107 if c.repo_info.stats:
105 if c.repo_info.stats:
108 # this is on what revision we ended up so we add +1 for count
106 # 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
107 last_rev = c.repo_info.stats.stat_on_revision + 1
110 else:
108 else:
111 last_rev = 0
109 last_rev = 0
112 c.stats_revision = last_rev
110 c.stats_revision = last_rev
113
111
114 c.repo_last_rev = repo.count() if repo.revisions else 0
112 c.repo_last_rev = repo.count() if repo.revisions else 0
115
113
116 if last_rev == 0 or c.repo_last_rev == 0:
114 if last_rev == 0 or c.repo_last_rev == 0:
117 c.stats_percentage = 0
115 c.stats_percentage = 0
118 else:
116 else:
119 c.stats_percentage = '%.2f' % ((float((last_rev)) /
117 c.stats_percentage = '%.2f' % ((float((last_rev)) /
120 c.repo_last_rev) * 100)
118 c.repo_last_rev) * 100)
121
119
122 c.repo_fields = RepositoryField.query()\
120 c.repo_fields = RepositoryField.query()\
123 .filter(RepositoryField.repository == db_repo).all()
121 .filter(RepositoryField.repository == db_repo).all()
124
122
125 defaults = RepoModel()._get_defaults(repo_name)
123 defaults = RepoModel()._get_defaults(repo_name)
126
124
127 c.repos_list = [('', _('--REMOVE FORK--'))]
125 c.repos_list = [('', _('--REMOVE FORK--'))]
128 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
129 Repository.query().order_by(Repository.repo_name).all()
127 Repository.query().order_by(Repository.repo_name).all()
130 if x.repo_id != c.repo_info.repo_id]
128 if x.repo_id != c.repo_info.repo_id]
131
129
132 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 ''
133 return defaults
131 return defaults
134
132
135 @HasPermissionAllDecorator('hg.admin')
133 @HasPermissionAllDecorator('hg.admin')
136 def index(self, format='html'):
134 def index(self, format='html'):
137 """GET /repos: All items in the collection"""
135 """GET /repos: All items in the collection"""
138 # url('repos')
136 # url('repos')
139
137
140 c.repos_list = Repository.query()\
138 c.repos_list = Repository.query()\
141 .order_by(func.lower(Repository.repo_name))\
139 .order_by(func.lower(Repository.repo_name))\
142 .all()
140 .all()
143
141
144 repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list,
142 repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list,
145 admin=True,
143 admin=True,
146 super_user_actions=True)
144 super_user_actions=True)
147 #json used to render the grid
145 #json used to render the grid
148 c.data = json.dumps(repos_data)
146 c.data = json.dumps(repos_data)
149
147
150 return render('admin/repos/repos.html')
148 return render('admin/repos/repos.html')
151
149
152 @NotAnonymous()
150 @NotAnonymous()
153 def create(self):
151 def create(self):
154 """
152 """
155 POST /repos: Create a new item"""
153 POST /repos: Create a new item"""
156 # url('repos')
154 # url('repos')
157
155
158 self.__load_defaults()
156 self.__load_defaults()
159 form_result = {}
157 form_result = {}
160 try:
158 try:
161 form_result = RepoForm(repo_groups=c.repo_groups_choices,
159 form_result = RepoForm(repo_groups=c.repo_groups_choices,
162 landing_revs=c.landing_revs_choices)()\
160 landing_revs=c.landing_revs_choices)()\
163 .to_python(dict(request.POST))
161 .to_python(dict(request.POST))
164
162
165 new_repo = RepoModel().create(form_result,
163 new_repo = RepoModel().create(form_result,
166 self.rhodecode_user.user_id)
164 self.rhodecode_user.user_id)
167 if form_result['clone_uri']:
165 if form_result['clone_uri']:
168 h.flash(_('Created repository %s from %s') \
166 h.flash(_('Created repository %s from %s') \
169 % (form_result['repo_name'], form_result['clone_uri']),
167 % (form_result['repo_name'], form_result['clone_uri']),
170 category='success')
168 category='success')
171 else:
169 else:
172 repo_url = h.link_to(form_result['repo_name'],
170 repo_url = h.link_to(form_result['repo_name'],
173 h.url('summary_home', repo_name=form_result['repo_name_full']))
171 h.url('summary_home', repo_name=form_result['repo_name_full']))
174 h.flash(h.literal(_('Created repository %s') % repo_url),
172 h.flash(h.literal(_('Created repository %s') % repo_url),
175 category='success')
173 category='success')
176
174
177 if request.POST.get('user_created'):
175 if request.POST.get('user_created'):
178 # created by regular non admin user
176 # created by regular non admin user
179 action_logger(self.rhodecode_user, 'user_created_repo',
177 action_logger(self.rhodecode_user, 'user_created_repo',
180 form_result['repo_name_full'], self.ip_addr,
178 form_result['repo_name_full'], self.ip_addr,
181 self.sa)
179 self.sa)
182 else:
180 else:
183 action_logger(self.rhodecode_user, 'admin_created_repo',
181 action_logger(self.rhodecode_user, 'admin_created_repo',
184 form_result['repo_name_full'], self.ip_addr,
182 form_result['repo_name_full'], self.ip_addr,
185 self.sa)
183 self.sa)
186 Session().commit()
184 Session().commit()
187 except formencode.Invalid, errors:
185 except formencode.Invalid, errors:
188 return htmlfill.render(
186 return htmlfill.render(
189 render('admin/repos/repo_add.html'),
187 render('admin/repos/repo_add.html'),
190 defaults=errors.value,
188 defaults=errors.value,
191 errors=errors.error_dict or {},
189 errors=errors.error_dict or {},
192 prefix_error=False,
190 prefix_error=False,
193 encoding="UTF-8")
191 encoding="UTF-8")
194
192
195 except Exception:
193 except Exception:
196 log.error(traceback.format_exc())
194 log.error(traceback.format_exc())
197 msg = _('Error creating repository %s') \
195 msg = _('Error creating repository %s') \
198 % form_result.get('repo_name')
196 % form_result.get('repo_name')
199 h.flash(msg, category='error')
197 h.flash(msg, category='error')
200 if c.rhodecode_user.is_admin:
198 if c.rhodecode_user.is_admin:
201 return redirect(url('repos'))
199 return redirect(url('repos'))
202 return redirect(url('home'))
200 return redirect(url('home'))
203 #redirect to our new repo !
201 #redirect to our new repo !
204 return redirect(url('summary_home', repo_name=new_repo.repo_name))
202 return redirect(url('summary_home', repo_name=new_repo.repo_name))
205
203
206 @NotAnonymous()
204 @NotAnonymous()
207 def create_repository(self):
205 def create_repository(self):
208 """GET /_admin/create_repository: Form to create a new item"""
206 """GET /_admin/create_repository: Form to create a new item"""
209 new_repo = request.GET.get('repo', '')
207 new_repo = request.GET.get('repo', '')
210 parent_group = request.GET.get('parent_group')
208 parent_group = request.GET.get('parent_group')
211 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
209 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
212 #you're not super admin nor have global create permissions,
210 #you're not super admin nor have global create permissions,
213 #but maybe you have at least write permission to a parent group ?
211 #but maybe you have at least write permission to a parent group ?
214 _gr = RepoGroup.get(parent_group)
212 _gr = RepoGroup.get(parent_group)
215 gr_name = _gr.group_name if _gr else None
213 gr_name = _gr.group_name if _gr else None
216 if not HasReposGroupPermissionAny('group.admin', 'group.write')(group_name=gr_name):
214 if not HasReposGroupPermissionAny('group.admin', 'group.write')(group_name=gr_name):
217 raise HTTPForbidden
215 raise HTTPForbidden
218
216
219 acl_groups = GroupList(RepoGroup.query().all(),
217 acl_groups = GroupList(RepoGroup.query().all(),
220 perm_set=['group.write', 'group.admin'])
218 perm_set=['group.write', 'group.admin'])
221 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
219 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
222 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
220 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
223 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
221 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
224
222
225 c.new_repo = repo_name_slug(new_repo)
223 c.new_repo = repo_name_slug(new_repo)
226
224
227 ## apply the defaults from defaults page
225 ## apply the defaults from defaults page
228 defaults = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
226 defaults = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
229 if parent_group:
227 if parent_group:
230 defaults.update({'repo_group': parent_group})
228 defaults.update({'repo_group': parent_group})
231
229
232 return htmlfill.render(
230 return htmlfill.render(
233 render('admin/repos/repo_add.html'),
231 render('admin/repos/repo_add.html'),
234 defaults=defaults,
232 defaults=defaults,
235 errors={},
233 errors={},
236 prefix_error=False,
234 prefix_error=False,
237 encoding="UTF-8"
235 encoding="UTF-8"
238 )
236 )
239
237
240 @HasRepoPermissionAllDecorator('repository.admin')
238 @HasRepoPermissionAllDecorator('repository.admin')
241 def update(self, repo_name):
239 def update(self, repo_name):
242 """
240 """
243 PUT /repos/repo_name: Update an existing item"""
241 PUT /repos/repo_name: Update an existing item"""
244 # Forms posted to this method should contain a hidden field:
242 # Forms posted to this method should contain a hidden field:
245 # <input type="hidden" name="_method" value="PUT" />
243 # <input type="hidden" name="_method" value="PUT" />
246 # Or using helpers:
244 # Or using helpers:
247 # h.form(url('repo', repo_name=ID),
245 # h.form(url('repo', repo_name=ID),
248 # method='put')
246 # method='put')
249 # url('repo', repo_name=ID)
247 # url('repo', repo_name=ID)
250 self.__load_defaults()
248 self.__load_defaults()
251 repo_model = RepoModel()
249 repo_model = RepoModel()
252 changed_name = repo_name
250 changed_name = repo_name
253 #override the choices with extracted revisions !
251 #override the choices with extracted revisions !
254 choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo_name)
252 choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo_name)
255 c.landing_revs_choices = choices
253 c.landing_revs_choices = choices
256 repo = Repository.get_by_repo_name(repo_name)
254 repo = Repository.get_by_repo_name(repo_name)
257 _form = RepoForm(edit=True, old_data={'repo_name': repo_name,
255 _form = RepoForm(edit=True, old_data={'repo_name': repo_name,
258 'repo_group': repo.group.get_dict() \
256 'repo_group': repo.group.get_dict() \
259 if repo.group else {}},
257 if repo.group else {}},
260 repo_groups=c.repo_groups_choices,
258 repo_groups=c.repo_groups_choices,
261 landing_revs=c.landing_revs_choices)()
259 landing_revs=c.landing_revs_choices)()
262 try:
260 try:
263 form_result = _form.to_python(dict(request.POST))
261 form_result = _form.to_python(dict(request.POST))
264 repo = repo_model.update(repo_name, **form_result)
262 repo = repo_model.update(repo_name, **form_result)
265 ScmModel().mark_for_invalidation(repo_name)
263 ScmModel().mark_for_invalidation(repo_name)
266 h.flash(_('Repository %s updated successfully') % repo_name,
264 h.flash(_('Repository %s updated successfully') % repo_name,
267 category='success')
265 category='success')
268 changed_name = repo.repo_name
266 changed_name = repo.repo_name
269 action_logger(self.rhodecode_user, 'admin_updated_repo',
267 action_logger(self.rhodecode_user, 'admin_updated_repo',
270 changed_name, self.ip_addr, self.sa)
268 changed_name, self.ip_addr, self.sa)
271 Session().commit()
269 Session().commit()
272 except formencode.Invalid, errors:
270 except formencode.Invalid, errors:
273 defaults = self.__load_data(repo_name)
271 defaults = self.__load_data(repo_name)
274 defaults.update(errors.value)
272 defaults.update(errors.value)
275 return htmlfill.render(
273 return htmlfill.render(
276 render('admin/repos/repo_edit.html'),
274 render('admin/repos/repo_edit.html'),
277 defaults=defaults,
275 defaults=defaults,
278 errors=errors.error_dict or {},
276 errors=errors.error_dict or {},
279 prefix_error=False,
277 prefix_error=False,
280 encoding="UTF-8")
278 encoding="UTF-8")
281
279
282 except Exception:
280 except Exception:
283 log.error(traceback.format_exc())
281 log.error(traceback.format_exc())
284 h.flash(_('Error occurred during update of repository %s') \
282 h.flash(_('Error occurred during update of repository %s') \
285 % repo_name, category='error')
283 % repo_name, category='error')
286 return redirect(url('edit_repo', repo_name=changed_name))
284 return redirect(url('edit_repo', repo_name=changed_name))
287
285
288 @HasRepoPermissionAllDecorator('repository.admin')
286 @HasRepoPermissionAllDecorator('repository.admin')
289 def delete(self, repo_name):
287 def delete(self, repo_name):
290 """
288 """
291 DELETE /repos/repo_name: Delete an existing item"""
289 DELETE /repos/repo_name: Delete an existing item"""
292 # Forms posted to this method should contain a hidden field:
290 # Forms posted to this method should contain a hidden field:
293 # <input type="hidden" name="_method" value="DELETE" />
291 # <input type="hidden" name="_method" value="DELETE" />
294 # Or using helpers:
292 # Or using helpers:
295 # h.form(url('repo', repo_name=ID),
293 # h.form(url('repo', repo_name=ID),
296 # method='delete')
294 # method='delete')
297 # url('repo', repo_name=ID)
295 # url('repo', repo_name=ID)
298
296
299 repo_model = RepoModel()
297 repo_model = RepoModel()
300 repo = repo_model.get_by_repo_name(repo_name)
298 repo = repo_model.get_by_repo_name(repo_name)
301 if not repo:
299 if not repo:
302 h.not_mapped_error(repo_name)
300 h.not_mapped_error(repo_name)
303 return redirect(url('repos'))
301 return redirect(url('repos'))
304 try:
302 try:
305 _forks = repo.forks.count()
303 _forks = repo.forks.count()
306 handle_forks = None
304 handle_forks = None
307 if _forks and request.POST.get('forks'):
305 if _forks and request.POST.get('forks'):
308 do = request.POST['forks']
306 do = request.POST['forks']
309 if do == 'detach_forks':
307 if do == 'detach_forks':
310 handle_forks = 'detach'
308 handle_forks = 'detach'
311 h.flash(_('Detached %s forks') % _forks, category='success')
309 h.flash(_('Detached %s forks') % _forks, category='success')
312 elif do == 'delete_forks':
310 elif do == 'delete_forks':
313 handle_forks = 'delete'
311 handle_forks = 'delete'
314 h.flash(_('Deleted %s forks') % _forks, category='success')
312 h.flash(_('Deleted %s forks') % _forks, category='success')
315 repo_model.delete(repo, forks=handle_forks)
313 repo_model.delete(repo, forks=handle_forks)
316 action_logger(self.rhodecode_user, 'admin_deleted_repo',
314 action_logger(self.rhodecode_user, 'admin_deleted_repo',
317 repo_name, self.ip_addr, self.sa)
315 repo_name, self.ip_addr, self.sa)
318 ScmModel().mark_for_invalidation(repo_name)
316 ScmModel().mark_for_invalidation(repo_name)
319 h.flash(_('Deleted repository %s') % repo_name, category='success')
317 h.flash(_('Deleted repository %s') % repo_name, category='success')
320 Session().commit()
318 Session().commit()
321 except AttachedForksError:
319 except AttachedForksError:
322 h.flash(_('Cannot delete %s it still contains attached forks')
320 h.flash(_('Cannot delete %s it still contains attached forks')
323 % repo_name, category='warning')
321 % repo_name, category='warning')
324
322
325 except Exception:
323 except Exception:
326 log.error(traceback.format_exc())
324 log.error(traceback.format_exc())
327 h.flash(_('An error occurred during deletion of %s') % repo_name,
325 h.flash(_('An error occurred during deletion of %s') % repo_name,
328 category='error')
326 category='error')
329
327
330 return redirect(url('repos'))
328 return redirect(url('repos'))
331
329
332 @HasRepoPermissionAllDecorator('repository.admin')
330 @HasRepoPermissionAllDecorator('repository.admin')
333 def set_repo_perm_member(self, repo_name):
331 def set_repo_perm_member(self, repo_name):
334 form = RepoPermsForm()().to_python(request.POST)
332 form = RepoPermsForm()().to_python(request.POST)
335
333
336 perms_new = form['perms_new']
334 perms_new = form['perms_new']
337 perms_updates = form['perms_updates']
335 perms_updates = form['perms_updates']
338 cur_repo = repo_name
336 cur_repo = repo_name
339
337
340 # update permissions
338 # update permissions
341 for member, perm, member_type in perms_updates:
339 for member, perm, member_type in perms_updates:
342 if member_type == 'user':
340 if member_type == 'user':
343 # this updates existing one
341 # this updates existing one
344 RepoModel().grant_user_permission(
342 RepoModel().grant_user_permission(
345 repo=cur_repo, user=member, perm=perm
343 repo=cur_repo, user=member, perm=perm
346 )
344 )
347 else:
345 else:
348 RepoModel().grant_users_group_permission(
346 RepoModel().grant_users_group_permission(
349 repo=cur_repo, group_name=member, perm=perm
347 repo=cur_repo, group_name=member, perm=perm
350 )
348 )
351 # set new permissions
349 # set new permissions
352 for member, perm, member_type in perms_new:
350 for member, perm, member_type in perms_new:
353 if member_type == 'user':
351 if member_type == 'user':
354 RepoModel().grant_user_permission(
352 RepoModel().grant_user_permission(
355 repo=cur_repo, user=member, perm=perm
353 repo=cur_repo, user=member, perm=perm
356 )
354 )
357 else:
355 else:
358 RepoModel().grant_users_group_permission(
356 RepoModel().grant_users_group_permission(
359 repo=cur_repo, group_name=member, perm=perm
357 repo=cur_repo, group_name=member, perm=perm
360 )
358 )
361 #TODO: implement this
359 #TODO: implement this
362 #action_logger(self.rhodecode_user, 'admin_changed_repo_permissions',
360 #action_logger(self.rhodecode_user, 'admin_changed_repo_permissions',
363 # repo_name, self.ip_addr, self.sa)
361 # repo_name, self.ip_addr, self.sa)
364 Session().commit()
362 Session().commit()
365 h.flash(_('Repository permissions updated'), category='success')
363 h.flash(_('Repository permissions updated'), category='success')
366 return redirect(url('edit_repo', repo_name=repo_name))
364 return redirect(url('edit_repo', repo_name=repo_name))
367
365
368 @HasRepoPermissionAllDecorator('repository.admin')
366 @HasRepoPermissionAllDecorator('repository.admin')
369 def delete_perm_user(self, repo_name):
367 def delete_perm_user(self, repo_name):
370 """
368 """
371 DELETE an existing repository permission user
369 DELETE an existing repository permission user
372
370
373 :param repo_name:
371 :param repo_name:
374 """
372 """
375 try:
373 try:
376 RepoModel().revoke_user_permission(repo=repo_name,
374 RepoModel().revoke_user_permission(repo=repo_name,
377 user=request.POST['user_id'])
375 user=request.POST['user_id'])
378 #TODO: implement this
376 #TODO: implement this
379 #action_logger(self.rhodecode_user, 'admin_revoked_repo_permissions',
377 #action_logger(self.rhodecode_user, 'admin_revoked_repo_permissions',
380 # repo_name, self.ip_addr, self.sa)
378 # repo_name, self.ip_addr, self.sa)
381 Session().commit()
379 Session().commit()
382 except Exception:
380 except Exception:
383 log.error(traceback.format_exc())
381 log.error(traceback.format_exc())
384 h.flash(_('An error occurred during deletion of repository user'),
382 h.flash(_('An error occurred during deletion of repository user'),
385 category='error')
383 category='error')
386 raise HTTPInternalServerError()
384 raise HTTPInternalServerError()
387
385
388 @HasRepoPermissionAllDecorator('repository.admin')
386 @HasRepoPermissionAllDecorator('repository.admin')
389 def delete_perm_users_group(self, repo_name):
387 def delete_perm_users_group(self, repo_name):
390 """
388 """
391 DELETE an existing repository permission user group
389 DELETE an existing repository permission user group
392
390
393 :param repo_name:
391 :param repo_name:
394 """
392 """
395
393
396 try:
394 try:
397 RepoModel().revoke_users_group_permission(
395 RepoModel().revoke_users_group_permission(
398 repo=repo_name, group_name=request.POST['users_group_id']
396 repo=repo_name, group_name=request.POST['users_group_id']
399 )
397 )
400 Session().commit()
398 Session().commit()
401 except Exception:
399 except Exception:
402 log.error(traceback.format_exc())
400 log.error(traceback.format_exc())
403 h.flash(_('An error occurred during deletion of repository'
401 h.flash(_('An error occurred during deletion of repository'
404 ' user groups'),
402 ' user groups'),
405 category='error')
403 category='error')
406 raise HTTPInternalServerError()
404 raise HTTPInternalServerError()
407
405
408 @HasRepoPermissionAllDecorator('repository.admin')
406 @HasRepoPermissionAllDecorator('repository.admin')
409 def repo_stats(self, repo_name):
407 def repo_stats(self, repo_name):
410 """
408 """
411 DELETE an existing repository statistics
409 DELETE an existing repository statistics
412
410
413 :param repo_name:
411 :param repo_name:
414 """
412 """
415
413
416 try:
414 try:
417 RepoModel().delete_stats(repo_name)
415 RepoModel().delete_stats(repo_name)
418 Session().commit()
416 Session().commit()
419 except Exception, e:
417 except Exception, e:
420 log.error(traceback.format_exc())
418 log.error(traceback.format_exc())
421 h.flash(_('An error occurred during deletion of repository stats'),
419 h.flash(_('An error occurred during deletion of repository stats'),
422 category='error')
420 category='error')
423 return redirect(url('edit_repo', repo_name=repo_name))
421 return redirect(url('edit_repo', repo_name=repo_name))
424
422
425 @HasRepoPermissionAllDecorator('repository.admin')
423 @HasRepoPermissionAllDecorator('repository.admin')
426 def repo_cache(self, repo_name):
424 def repo_cache(self, repo_name):
427 """
425 """
428 INVALIDATE existing repository cache
426 INVALIDATE existing repository cache
429
427
430 :param repo_name:
428 :param repo_name:
431 """
429 """
432
430
433 try:
431 try:
434 ScmModel().mark_for_invalidation(repo_name)
432 ScmModel().mark_for_invalidation(repo_name)
435 Session().commit()
433 Session().commit()
436 except Exception, e:
434 except Exception, e:
437 log.error(traceback.format_exc())
435 log.error(traceback.format_exc())
438 h.flash(_('An error occurred during cache invalidation'),
436 h.flash(_('An error occurred during cache invalidation'),
439 category='error')
437 category='error')
440 return redirect(url('edit_repo', repo_name=repo_name))
438 return redirect(url('edit_repo', repo_name=repo_name))
441
439
442 @HasRepoPermissionAllDecorator('repository.admin')
440 @HasRepoPermissionAllDecorator('repository.admin')
443 def repo_locking(self, repo_name):
441 def repo_locking(self, repo_name):
444 """
442 """
445 Unlock repository when it is locked !
443 Unlock repository when it is locked !
446
444
447 :param repo_name:
445 :param repo_name:
448 """
446 """
449
447
450 try:
448 try:
451 repo = Repository.get_by_repo_name(repo_name)
449 repo = Repository.get_by_repo_name(repo_name)
452 if request.POST.get('set_lock'):
450 if request.POST.get('set_lock'):
453 Repository.lock(repo, c.rhodecode_user.user_id)
451 Repository.lock(repo, c.rhodecode_user.user_id)
454 elif request.POST.get('set_unlock'):
452 elif request.POST.get('set_unlock'):
455 Repository.unlock(repo)
453 Repository.unlock(repo)
456 except Exception, e:
454 except Exception, e:
457 log.error(traceback.format_exc())
455 log.error(traceback.format_exc())
458 h.flash(_('An error occurred during unlocking'),
456 h.flash(_('An error occurred during unlocking'),
459 category='error')
457 category='error')
460 return redirect(url('edit_repo', repo_name=repo_name))
458 return redirect(url('edit_repo', repo_name=repo_name))
461
459
462 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
460 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
463 def toggle_locking(self, repo_name):
461 def toggle_locking(self, repo_name):
464 """
462 """
465 Toggle locking of repository by simple GET call to url
463 Toggle locking of repository by simple GET call to url
466
464
467 :param repo_name:
465 :param repo_name:
468 """
466 """
469
467
470 try:
468 try:
471 repo = Repository.get_by_repo_name(repo_name)
469 repo = Repository.get_by_repo_name(repo_name)
472
470
473 if repo.enable_locking:
471 if repo.enable_locking:
474 if repo.locked[0]:
472 if repo.locked[0]:
475 Repository.unlock(repo)
473 Repository.unlock(repo)
476 action = _('Unlocked')
474 action = _('Unlocked')
477 else:
475 else:
478 Repository.lock(repo, c.rhodecode_user.user_id)
476 Repository.lock(repo, c.rhodecode_user.user_id)
479 action = _('Locked')
477 action = _('Locked')
480
478
481 h.flash(_('Repository has been %s') % action,
479 h.flash(_('Repository has been %s') % action,
482 category='success')
480 category='success')
483 except Exception, e:
481 except Exception, e:
484 log.error(traceback.format_exc())
482 log.error(traceback.format_exc())
485 h.flash(_('An error occurred during unlocking'),
483 h.flash(_('An error occurred during unlocking'),
486 category='error')
484 category='error')
487 return redirect(url('summary_home', repo_name=repo_name))
485 return redirect(url('summary_home', repo_name=repo_name))
488
486
489 @HasRepoPermissionAllDecorator('repository.admin')
487 @HasRepoPermissionAllDecorator('repository.admin')
490 def repo_public_journal(self, repo_name):
488 def repo_public_journal(self, repo_name):
491 """
489 """
492 Set's this repository to be visible in public journal,
490 Set's this repository to be visible in public journal,
493 in other words assing default user to follow this repo
491 in other words assing default user to follow this repo
494
492
495 :param repo_name:
493 :param repo_name:
496 """
494 """
497
495
498 cur_token = request.POST.get('auth_token')
496 cur_token = request.POST.get('auth_token')
499 token = get_token()
497 token = get_token()
500 if cur_token == token:
498 if cur_token == token:
501 try:
499 try:
502 repo_id = Repository.get_by_repo_name(repo_name).repo_id
500 repo_id = Repository.get_by_repo_name(repo_name).repo_id
503 user_id = User.get_by_username('default').user_id
501 user_id = User.get_by_username('default').user_id
504 self.scm_model.toggle_following_repo(repo_id, user_id)
502 self.scm_model.toggle_following_repo(repo_id, user_id)
505 h.flash(_('Updated repository visibility in public journal'),
503 h.flash(_('Updated repository visibility in public journal'),
506 category='success')
504 category='success')
507 Session().commit()
505 Session().commit()
508 except Exception:
506 except Exception:
509 h.flash(_('An error occurred during setting this'
507 h.flash(_('An error occurred during setting this'
510 ' repository in public journal'),
508 ' repository in public journal'),
511 category='error')
509 category='error')
512
510
513 else:
511 else:
514 h.flash(_('Token mismatch'), category='error')
512 h.flash(_('Token mismatch'), category='error')
515 return redirect(url('edit_repo', repo_name=repo_name))
513 return redirect(url('edit_repo', repo_name=repo_name))
516
514
517 @HasRepoPermissionAllDecorator('repository.admin')
515 @HasRepoPermissionAllDecorator('repository.admin')
518 def repo_pull(self, repo_name):
516 def repo_pull(self, repo_name):
519 """
517 """
520 Runs task to update given repository with remote changes,
518 Runs task to update given repository with remote changes,
521 ie. make pull on remote location
519 ie. make pull on remote location
522
520
523 :param repo_name:
521 :param repo_name:
524 """
522 """
525 try:
523 try:
526 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
524 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
527 h.flash(_('Pulled from remote location'), category='success')
525 h.flash(_('Pulled from remote location'), category='success')
528 except Exception, e:
526 except Exception, e:
529 h.flash(_('An error occurred during pull from remote location'),
527 h.flash(_('An error occurred during pull from remote location'),
530 category='error')
528 category='error')
531
529
532 return redirect(url('edit_repo', repo_name=repo_name))
530 return redirect(url('edit_repo', repo_name=repo_name))
533
531
534 @HasRepoPermissionAllDecorator('repository.admin')
532 @HasRepoPermissionAllDecorator('repository.admin')
535 def repo_as_fork(self, repo_name):
533 def repo_as_fork(self, repo_name):
536 """
534 """
537 Mark given repository as a fork of another
535 Mark given repository as a fork of another
538
536
539 :param repo_name:
537 :param repo_name:
540 """
538 """
541 try:
539 try:
542 fork_id = request.POST.get('id_fork_of')
540 fork_id = request.POST.get('id_fork_of')
543 repo = ScmModel().mark_as_fork(repo_name, fork_id,
541 repo = ScmModel().mark_as_fork(repo_name, fork_id,
544 self.rhodecode_user.username)
542 self.rhodecode_user.username)
545 fork = repo.fork.repo_name if repo.fork else _('Nothing')
543 fork = repo.fork.repo_name if repo.fork else _('Nothing')
546 Session().commit()
544 Session().commit()
547 h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork),
545 h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork),
548 category='success')
546 category='success')
549 except Exception, e:
547 except Exception, e:
550 log.error(traceback.format_exc())
548 log.error(traceback.format_exc())
551 h.flash(_('An error occurred during this operation'),
549 h.flash(_('An error occurred during this operation'),
552 category='error')
550 category='error')
553
551
554 return redirect(url('edit_repo', repo_name=repo_name))
552 return redirect(url('edit_repo', repo_name=repo_name))
555
553
556 @HasPermissionAllDecorator('hg.admin')
554 @HasPermissionAllDecorator('hg.admin')
557 def show(self, repo_name, format='html'):
555 def show(self, repo_name, format='html'):
558 """GET /repos/repo_name: Show a specific item"""
556 """GET /repos/repo_name: Show a specific item"""
559 # url('repo', repo_name=ID)
557 # url('repo', repo_name=ID)
560
558
561 @HasRepoPermissionAllDecorator('repository.admin')
559 @HasRepoPermissionAllDecorator('repository.admin')
562 def edit(self, repo_name, format='html'):
560 def edit(self, repo_name, format='html'):
563 """GET /repos/repo_name/edit: Form to edit an existing item"""
561 """GET /repos/repo_name/edit: Form to edit an existing item"""
564 # url('edit_repo', repo_name=ID)
562 # url('edit_repo', repo_name=ID)
565 defaults = self.__load_data(repo_name)
563 defaults = self.__load_data(repo_name)
566
564
567 return htmlfill.render(
565 return htmlfill.render(
568 render('admin/repos/repo_edit.html'),
566 render('admin/repos/repo_edit.html'),
569 defaults=defaults,
567 defaults=defaults,
570 encoding="UTF-8",
568 encoding="UTF-8",
571 force_defaults=False
569 force_defaults=False
572 )
570 )
573
571
574 @HasPermissionAllDecorator('hg.admin')
572 @HasPermissionAllDecorator('hg.admin')
575 def create_repo_field(self, repo_name):
573 def create_repo_field(self, repo_name):
576 try:
574 try:
577 form_result = RepoFieldForm()().to_python(dict(request.POST))
575 form_result = RepoFieldForm()().to_python(dict(request.POST))
578 new_field = RepositoryField()
576 new_field = RepositoryField()
579 new_field.repository = Repository.get_by_repo_name(repo_name)
577 new_field.repository = Repository.get_by_repo_name(repo_name)
580 new_field.field_key = form_result['new_field_key']
578 new_field.field_key = form_result['new_field_key']
581 new_field.field_type = form_result['new_field_type'] # python type
579 new_field.field_type = form_result['new_field_type'] # python type
582 new_field.field_value = form_result['new_field_value'] # set initial blank value
580 new_field.field_value = form_result['new_field_value'] # set initial blank value
583 new_field.field_desc = form_result['new_field_desc']
581 new_field.field_desc = form_result['new_field_desc']
584 new_field.field_label = form_result['new_field_label']
582 new_field.field_label = form_result['new_field_label']
585 Session().add(new_field)
583 Session().add(new_field)
586 Session().commit()
584 Session().commit()
587
585
588 except Exception, e:
586 except Exception, e:
589 log.error(traceback.format_exc())
587 log.error(traceback.format_exc())
590 msg = _('An error occurred during creation of field')
588 msg = _('An error occurred during creation of field')
591 if isinstance(e, formencode.Invalid):
589 if isinstance(e, formencode.Invalid):
592 msg += ". " + e.msg
590 msg += ". " + e.msg
593 h.flash(msg, category='error')
591 h.flash(msg, category='error')
594 return redirect(url('edit_repo', repo_name=repo_name))
592 return redirect(url('edit_repo', repo_name=repo_name))
595
593
596 @HasPermissionAllDecorator('hg.admin')
594 @HasPermissionAllDecorator('hg.admin')
597 def delete_repo_field(self, repo_name, field_id):
595 def delete_repo_field(self, repo_name, field_id):
598 field = RepositoryField.get_or_404(field_id)
596 field = RepositoryField.get_or_404(field_id)
599 try:
597 try:
600 Session().delete(field)
598 Session().delete(field)
601 Session().commit()
599 Session().commit()
602 except Exception, e:
600 except Exception, e:
603 log.error(traceback.format_exc())
601 log.error(traceback.format_exc())
604 msg = _('An error occurred during removal of field')
602 msg = _('An error occurred during removal of field')
605 h.flash(msg, category='error')
603 h.flash(msg, category='error')
606 return redirect(url('edit_repo', repo_name=repo_name))
604 return redirect(url('edit_repo', repo_name=repo_name))
@@ -1,515 +1,513 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, HasPermissionAny,\
40 HasPermissionAnyDecorator, NotAnonymous, HasPermissionAny,\
41 HasReposGroupPermissionAll, HasReposGroupPermissionAny, AuthUser
41 HasReposGroupPermissionAll, HasReposGroupPermissionAny, AuthUser
42 from rhodecode.lib.base import BaseController, render
42 from rhodecode.lib.base import BaseController, render
43 from rhodecode.lib.celerylib import tasks, run_task
43 from rhodecode.lib.celerylib import tasks, run_task
44 from rhodecode.lib.utils import repo2db_mapper, set_rhodecode_config, \
44 from rhodecode.lib.utils import repo2db_mapper, set_rhodecode_config, \
45 check_git_version
45 check_git_version
46 from rhodecode.model.db import RhodeCodeUi, Repository, RepoGroup, \
46 from rhodecode.model.db import RhodeCodeUi, Repository, RepoGroup, \
47 RhodeCodeSetting, PullRequest, PullRequestReviewers
47 RhodeCodeSetting, PullRequest, PullRequestReviewers
48 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
48 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
49 ApplicationUiSettingsForm, ApplicationVisualisationForm
49 ApplicationUiSettingsForm, ApplicationVisualisationForm
50 from rhodecode.model.scm import ScmModel, GroupList
50 from rhodecode.model.scm import ScmModel, GroupList
51 from rhodecode.model.user import UserModel
51 from rhodecode.model.user import UserModel
52 from rhodecode.model.repo import RepoModel
52 from rhodecode.model.repo import RepoModel
53 from rhodecode.model.db import User
53 from rhodecode.model.db import User
54 from rhodecode.model.notification import EmailNotificationModel
54 from rhodecode.model.notification import EmailNotificationModel
55 from rhodecode.model.meta import Session
55 from rhodecode.model.meta import Session
56 from rhodecode.lib.utils2 import str2bool, safe_unicode
56 from rhodecode.lib.utils2 import str2bool, safe_unicode
57 from rhodecode.lib.compat import json
57 from rhodecode.lib.compat import json
58 log = logging.getLogger(__name__)
58 log = logging.getLogger(__name__)
59
59
60
60
61 class SettingsController(BaseController):
61 class SettingsController(BaseController):
62 """REST Controller styled on the Atom Publishing Protocol"""
62 """REST Controller styled on the Atom Publishing Protocol"""
63 # To properly map this controller, ensure your config/routing.py
63 # To properly map this controller, ensure your config/routing.py
64 # file has a resource setup:
64 # file has a resource setup:
65 # map.resource('setting', 'settings', controller='admin/settings',
65 # map.resource('setting', 'settings', controller='admin/settings',
66 # path_prefix='/admin', name_prefix='admin_')
66 # path_prefix='/admin', name_prefix='admin_')
67
67
68 @LoginRequired()
68 @LoginRequired()
69 def __before__(self):
69 def __before__(self):
70 c.admin_user = session.get('admin_user')
70 super(SettingsController, self).__before__()
71 c.admin_username = session.get('admin_username')
72 c.modules = sorted([(p.project_name, p.version)
71 c.modules = sorted([(p.project_name, p.version)
73 for p in pkg_resources.working_set]
72 for p in pkg_resources.working_set]
74 + [('git', check_git_version())],
73 + [('git', check_git_version())],
75 key=lambda k: k[0].lower())
74 key=lambda k: k[0].lower())
76 c.py_version = platform.python_version()
75 c.py_version = platform.python_version()
77 c.platform = platform.platform()
76 c.platform = platform.platform()
78 super(SettingsController, self).__before__()
79
77
80 @HasPermissionAllDecorator('hg.admin')
78 @HasPermissionAllDecorator('hg.admin')
81 def index(self, format='html'):
79 def index(self, format='html'):
82 """GET /admin/settings: All items in the collection"""
80 """GET /admin/settings: All items in the collection"""
83 # url('admin_settings')
81 # url('admin_settings')
84
82
85 defaults = RhodeCodeSetting.get_app_settings()
83 defaults = RhodeCodeSetting.get_app_settings()
86 defaults.update(self._get_hg_ui_settings())
84 defaults.update(self._get_hg_ui_settings())
87
85
88 return htmlfill.render(
86 return htmlfill.render(
89 render('admin/settings/settings.html'),
87 render('admin/settings/settings.html'),
90 defaults=defaults,
88 defaults=defaults,
91 encoding="UTF-8",
89 encoding="UTF-8",
92 force_defaults=False
90 force_defaults=False
93 )
91 )
94
92
95 @HasPermissionAllDecorator('hg.admin')
93 @HasPermissionAllDecorator('hg.admin')
96 def create(self):
94 def create(self):
97 """POST /admin/settings: Create a new item"""
95 """POST /admin/settings: Create a new item"""
98 # url('admin_settings')
96 # url('admin_settings')
99
97
100 @HasPermissionAllDecorator('hg.admin')
98 @HasPermissionAllDecorator('hg.admin')
101 def new(self, format='html'):
99 def new(self, format='html'):
102 """GET /admin/settings/new: Form to create a new item"""
100 """GET /admin/settings/new: Form to create a new item"""
103 # url('admin_new_setting')
101 # url('admin_new_setting')
104
102
105 @HasPermissionAllDecorator('hg.admin')
103 @HasPermissionAllDecorator('hg.admin')
106 def update(self, setting_id):
104 def update(self, setting_id):
107 """PUT /admin/settings/setting_id: Update an existing item"""
105 """PUT /admin/settings/setting_id: Update an existing item"""
108 # Forms posted to this method should contain a hidden field:
106 # Forms posted to this method should contain a hidden field:
109 # <input type="hidden" name="_method" value="PUT" />
107 # <input type="hidden" name="_method" value="PUT" />
110 # Or using helpers:
108 # Or using helpers:
111 # h.form(url('admin_setting', setting_id=ID),
109 # h.form(url('admin_setting', setting_id=ID),
112 # method='put')
110 # method='put')
113 # url('admin_setting', setting_id=ID)
111 # url('admin_setting', setting_id=ID)
114
112
115 if setting_id == 'mapping':
113 if setting_id == 'mapping':
116 rm_obsolete = request.POST.get('destroy', False)
114 rm_obsolete = request.POST.get('destroy', False)
117 log.debug('Rescanning directories with destroy=%s' % rm_obsolete)
115 log.debug('Rescanning directories with destroy=%s' % rm_obsolete)
118 initial = ScmModel().repo_scan()
116 initial = ScmModel().repo_scan()
119 log.debug('invalidating all repositories')
117 log.debug('invalidating all repositories')
120 for repo_name in initial.keys():
118 for repo_name in initial.keys():
121 ScmModel().mark_for_invalidation(repo_name)
119 ScmModel().mark_for_invalidation(repo_name)
122
120
123 added, removed = repo2db_mapper(initial, rm_obsolete)
121 added, removed = repo2db_mapper(initial, rm_obsolete)
124 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
122 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
125 h.flash(_('Repositories successfully '
123 h.flash(_('Repositories successfully '
126 'rescanned added: %s ; removed: %s') %
124 'rescanned added: %s ; removed: %s') %
127 (_repr(added), _repr(removed)),
125 (_repr(added), _repr(removed)),
128 category='success')
126 category='success')
129
127
130 if setting_id == 'whoosh':
128 if setting_id == 'whoosh':
131 repo_location = self._get_hg_ui_settings()['paths_root_path']
129 repo_location = self._get_hg_ui_settings()['paths_root_path']
132 full_index = request.POST.get('full_index', False)
130 full_index = request.POST.get('full_index', False)
133 run_task(tasks.whoosh_index, repo_location, full_index)
131 run_task(tasks.whoosh_index, repo_location, full_index)
134 h.flash(_('Whoosh reindex task scheduled'), category='success')
132 h.flash(_('Whoosh reindex task scheduled'), category='success')
135
133
136 if setting_id == 'global':
134 if setting_id == 'global':
137
135
138 application_form = ApplicationSettingsForm()()
136 application_form = ApplicationSettingsForm()()
139 try:
137 try:
140 form_result = application_form.to_python(dict(request.POST))
138 form_result = application_form.to_python(dict(request.POST))
141 except formencode.Invalid, errors:
139 except formencode.Invalid, errors:
142 return htmlfill.render(
140 return htmlfill.render(
143 render('admin/settings/settings.html'),
141 render('admin/settings/settings.html'),
144 defaults=errors.value,
142 defaults=errors.value,
145 errors=errors.error_dict or {},
143 errors=errors.error_dict or {},
146 prefix_error=False,
144 prefix_error=False,
147 encoding="UTF-8"
145 encoding="UTF-8"
148 )
146 )
149
147
150 try:
148 try:
151 sett1 = RhodeCodeSetting.get_by_name_or_create('title')
149 sett1 = RhodeCodeSetting.get_by_name_or_create('title')
152 sett1.app_settings_value = form_result['rhodecode_title']
150 sett1.app_settings_value = form_result['rhodecode_title']
153 Session().add(sett1)
151 Session().add(sett1)
154
152
155 sett2 = RhodeCodeSetting.get_by_name_or_create('realm')
153 sett2 = RhodeCodeSetting.get_by_name_or_create('realm')
156 sett2.app_settings_value = form_result['rhodecode_realm']
154 sett2.app_settings_value = form_result['rhodecode_realm']
157 Session().add(sett2)
155 Session().add(sett2)
158
156
159 sett3 = RhodeCodeSetting.get_by_name_or_create('ga_code')
157 sett3 = RhodeCodeSetting.get_by_name_or_create('ga_code')
160 sett3.app_settings_value = form_result['rhodecode_ga_code']
158 sett3.app_settings_value = form_result['rhodecode_ga_code']
161 Session().add(sett3)
159 Session().add(sett3)
162
160
163 Session().commit()
161 Session().commit()
164 set_rhodecode_config(config)
162 set_rhodecode_config(config)
165 h.flash(_('Updated application settings'), category='success')
163 h.flash(_('Updated application settings'), category='success')
166
164
167 except Exception:
165 except Exception:
168 log.error(traceback.format_exc())
166 log.error(traceback.format_exc())
169 h.flash(_('Error occurred during updating '
167 h.flash(_('Error occurred during updating '
170 'application settings'),
168 'application settings'),
171 category='error')
169 category='error')
172
170
173 if setting_id == 'visual':
171 if setting_id == 'visual':
174
172
175 application_form = ApplicationVisualisationForm()()
173 application_form = ApplicationVisualisationForm()()
176 try:
174 try:
177 form_result = application_form.to_python(dict(request.POST))
175 form_result = application_form.to_python(dict(request.POST))
178 except formencode.Invalid, errors:
176 except formencode.Invalid, errors:
179 return htmlfill.render(
177 return htmlfill.render(
180 render('admin/settings/settings.html'),
178 render('admin/settings/settings.html'),
181 defaults=errors.value,
179 defaults=errors.value,
182 errors=errors.error_dict or {},
180 errors=errors.error_dict or {},
183 prefix_error=False,
181 prefix_error=False,
184 encoding="UTF-8"
182 encoding="UTF-8"
185 )
183 )
186
184
187 try:
185 try:
188 sett1 = RhodeCodeSetting.get_by_name_or_create('show_public_icon')
186 sett1 = RhodeCodeSetting.get_by_name_or_create('show_public_icon')
189 sett1.app_settings_value = \
187 sett1.app_settings_value = \
190 form_result['rhodecode_show_public_icon']
188 form_result['rhodecode_show_public_icon']
191 Session().add(sett1)
189 Session().add(sett1)
192
190
193 sett2 = RhodeCodeSetting.get_by_name_or_create('show_private_icon')
191 sett2 = RhodeCodeSetting.get_by_name_or_create('show_private_icon')
194 sett2.app_settings_value = \
192 sett2.app_settings_value = \
195 form_result['rhodecode_show_private_icon']
193 form_result['rhodecode_show_private_icon']
196 Session().add(sett2)
194 Session().add(sett2)
197
195
198 sett3 = RhodeCodeSetting.get_by_name_or_create('stylify_metatags')
196 sett3 = RhodeCodeSetting.get_by_name_or_create('stylify_metatags')
199 sett3.app_settings_value = \
197 sett3.app_settings_value = \
200 form_result['rhodecode_stylify_metatags']
198 form_result['rhodecode_stylify_metatags']
201 Session().add(sett3)
199 Session().add(sett3)
202
200
203 sett4 = RhodeCodeSetting.get_by_name_or_create('lightweight_dashboard')
201 sett4 = RhodeCodeSetting.get_by_name_or_create('lightweight_dashboard')
204 sett4.app_settings_value = \
202 sett4.app_settings_value = \
205 form_result['rhodecode_lightweight_dashboard']
203 form_result['rhodecode_lightweight_dashboard']
206 Session().add(sett4)
204 Session().add(sett4)
207
205
208 sett4 = RhodeCodeSetting.get_by_name_or_create('repository_fields')
206 sett4 = RhodeCodeSetting.get_by_name_or_create('repository_fields')
209 sett4.app_settings_value = \
207 sett4.app_settings_value = \
210 form_result['rhodecode_repository_fields']
208 form_result['rhodecode_repository_fields']
211 Session().add(sett4)
209 Session().add(sett4)
212
210
213 Session().commit()
211 Session().commit()
214 set_rhodecode_config(config)
212 set_rhodecode_config(config)
215 h.flash(_('Updated visualisation settings'),
213 h.flash(_('Updated visualisation settings'),
216 category='success')
214 category='success')
217
215
218 except Exception:
216 except Exception:
219 log.error(traceback.format_exc())
217 log.error(traceback.format_exc())
220 h.flash(_('Error occurred during updating '
218 h.flash(_('Error occurred during updating '
221 'visualisation settings'),
219 'visualisation settings'),
222 category='error')
220 category='error')
223
221
224 if setting_id == 'vcs':
222 if setting_id == 'vcs':
225 application_form = ApplicationUiSettingsForm()()
223 application_form = ApplicationUiSettingsForm()()
226 try:
224 try:
227 form_result = application_form.to_python(dict(request.POST))
225 form_result = application_form.to_python(dict(request.POST))
228 except formencode.Invalid, errors:
226 except formencode.Invalid, errors:
229 return htmlfill.render(
227 return htmlfill.render(
230 render('admin/settings/settings.html'),
228 render('admin/settings/settings.html'),
231 defaults=errors.value,
229 defaults=errors.value,
232 errors=errors.error_dict or {},
230 errors=errors.error_dict or {},
233 prefix_error=False,
231 prefix_error=False,
234 encoding="UTF-8"
232 encoding="UTF-8"
235 )
233 )
236
234
237 try:
235 try:
238 sett = RhodeCodeUi.get_by_key('push_ssl')
236 sett = RhodeCodeUi.get_by_key('push_ssl')
239 sett.ui_value = form_result['web_push_ssl']
237 sett.ui_value = form_result['web_push_ssl']
240 Session().add(sett)
238 Session().add(sett)
241
239
242 sett = RhodeCodeUi.get_by_key('/')
240 sett = RhodeCodeUi.get_by_key('/')
243 sett.ui_value = form_result['paths_root_path']
241 sett.ui_value = form_result['paths_root_path']
244 Session().add(sett)
242 Session().add(sett)
245
243
246 #HOOKS
244 #HOOKS
247 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_UPDATE)
245 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_UPDATE)
248 sett.ui_active = form_result['hooks_changegroup_update']
246 sett.ui_active = form_result['hooks_changegroup_update']
249 Session().add(sett)
247 Session().add(sett)
250
248
251 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_REPO_SIZE)
249 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_REPO_SIZE)
252 sett.ui_active = form_result['hooks_changegroup_repo_size']
250 sett.ui_active = form_result['hooks_changegroup_repo_size']
253 Session().add(sett)
251 Session().add(sett)
254
252
255 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_PUSH)
253 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_PUSH)
256 sett.ui_active = form_result['hooks_changegroup_push_logger']
254 sett.ui_active = form_result['hooks_changegroup_push_logger']
257 Session().add(sett)
255 Session().add(sett)
258
256
259 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_PULL)
257 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_PULL)
260 sett.ui_active = form_result['hooks_outgoing_pull_logger']
258 sett.ui_active = form_result['hooks_outgoing_pull_logger']
261
259
262 Session().add(sett)
260 Session().add(sett)
263
261
264 ## EXTENSIONS
262 ## EXTENSIONS
265 sett = RhodeCodeUi.get_by_key('largefiles')
263 sett = RhodeCodeUi.get_by_key('largefiles')
266 if not sett:
264 if not sett:
267 #make one if it's not there !
265 #make one if it's not there !
268 sett = RhodeCodeUi()
266 sett = RhodeCodeUi()
269 sett.ui_key = 'largefiles'
267 sett.ui_key = 'largefiles'
270 sett.ui_section = 'extensions'
268 sett.ui_section = 'extensions'
271 sett.ui_active = form_result['extensions_largefiles']
269 sett.ui_active = form_result['extensions_largefiles']
272 Session().add(sett)
270 Session().add(sett)
273
271
274 sett = RhodeCodeUi.get_by_key('hgsubversion')
272 sett = RhodeCodeUi.get_by_key('hgsubversion')
275 if not sett:
273 if not sett:
276 #make one if it's not there !
274 #make one if it's not there !
277 sett = RhodeCodeUi()
275 sett = RhodeCodeUi()
278 sett.ui_key = 'hgsubversion'
276 sett.ui_key = 'hgsubversion'
279 sett.ui_section = 'extensions'
277 sett.ui_section = 'extensions'
280
278
281 sett.ui_active = form_result['extensions_hgsubversion']
279 sett.ui_active = form_result['extensions_hgsubversion']
282 Session().add(sett)
280 Session().add(sett)
283
281
284 # sett = RhodeCodeUi.get_by_key('hggit')
282 # sett = RhodeCodeUi.get_by_key('hggit')
285 # if not sett:
283 # if not sett:
286 # #make one if it's not there !
284 # #make one if it's not there !
287 # sett = RhodeCodeUi()
285 # sett = RhodeCodeUi()
288 # sett.ui_key = 'hggit'
286 # sett.ui_key = 'hggit'
289 # sett.ui_section = 'extensions'
287 # sett.ui_section = 'extensions'
290 #
288 #
291 # sett.ui_active = form_result['extensions_hggit']
289 # sett.ui_active = form_result['extensions_hggit']
292 # Session().add(sett)
290 # Session().add(sett)
293
291
294 Session().commit()
292 Session().commit()
295
293
296 h.flash(_('Updated VCS settings'), category='success')
294 h.flash(_('Updated VCS settings'), category='success')
297
295
298 except Exception:
296 except Exception:
299 log.error(traceback.format_exc())
297 log.error(traceback.format_exc())
300 h.flash(_('Error occurred during updating '
298 h.flash(_('Error occurred during updating '
301 'application settings'), category='error')
299 'application settings'), category='error')
302
300
303 if setting_id == 'hooks':
301 if setting_id == 'hooks':
304 ui_key = request.POST.get('new_hook_ui_key')
302 ui_key = request.POST.get('new_hook_ui_key')
305 ui_value = request.POST.get('new_hook_ui_value')
303 ui_value = request.POST.get('new_hook_ui_value')
306 try:
304 try:
307
305
308 if ui_value and ui_key:
306 if ui_value and ui_key:
309 RhodeCodeUi.create_or_update_hook(ui_key, ui_value)
307 RhodeCodeUi.create_or_update_hook(ui_key, ui_value)
310 h.flash(_('Added new hook'),
308 h.flash(_('Added new hook'),
311 category='success')
309 category='success')
312
310
313 # check for edits
311 # check for edits
314 update = False
312 update = False
315 _d = request.POST.dict_of_lists()
313 _d = request.POST.dict_of_lists()
316 for k, v in zip(_d.get('hook_ui_key', []),
314 for k, v in zip(_d.get('hook_ui_key', []),
317 _d.get('hook_ui_value_new', [])):
315 _d.get('hook_ui_value_new', [])):
318 RhodeCodeUi.create_or_update_hook(k, v)
316 RhodeCodeUi.create_or_update_hook(k, v)
319 update = True
317 update = True
320
318
321 if update:
319 if update:
322 h.flash(_('Updated hooks'), category='success')
320 h.flash(_('Updated hooks'), category='success')
323 Session().commit()
321 Session().commit()
324 except Exception:
322 except Exception:
325 log.error(traceback.format_exc())
323 log.error(traceback.format_exc())
326 h.flash(_('Error occurred during hook creation'),
324 h.flash(_('Error occurred during hook creation'),
327 category='error')
325 category='error')
328
326
329 return redirect(url('admin_edit_setting', setting_id='hooks'))
327 return redirect(url('admin_edit_setting', setting_id='hooks'))
330
328
331 if setting_id == 'email':
329 if setting_id == 'email':
332 test_email = request.POST.get('test_email')
330 test_email = request.POST.get('test_email')
333 test_email_subj = 'RhodeCode TestEmail'
331 test_email_subj = 'RhodeCode TestEmail'
334 test_email_body = 'RhodeCode Email test'
332 test_email_body = 'RhodeCode Email test'
335
333
336 test_email_html_body = EmailNotificationModel()\
334 test_email_html_body = EmailNotificationModel()\
337 .get_email_tmpl(EmailNotificationModel.TYPE_DEFAULT,
335 .get_email_tmpl(EmailNotificationModel.TYPE_DEFAULT,
338 body=test_email_body)
336 body=test_email_body)
339
337
340 recipients = [test_email] if test_email else None
338 recipients = [test_email] if test_email else None
341
339
342 run_task(tasks.send_email, recipients, test_email_subj,
340 run_task(tasks.send_email, recipients, test_email_subj,
343 test_email_body, test_email_html_body)
341 test_email_body, test_email_html_body)
344
342
345 h.flash(_('Email task created'), category='success')
343 h.flash(_('Email task created'), category='success')
346 return redirect(url('admin_settings'))
344 return redirect(url('admin_settings'))
347
345
348 @HasPermissionAllDecorator('hg.admin')
346 @HasPermissionAllDecorator('hg.admin')
349 def delete(self, setting_id):
347 def delete(self, setting_id):
350 """DELETE /admin/settings/setting_id: Delete an existing item"""
348 """DELETE /admin/settings/setting_id: Delete an existing item"""
351 # Forms posted to this method should contain a hidden field:
349 # Forms posted to this method should contain a hidden field:
352 # <input type="hidden" name="_method" value="DELETE" />
350 # <input type="hidden" name="_method" value="DELETE" />
353 # Or using helpers:
351 # Or using helpers:
354 # h.form(url('admin_setting', setting_id=ID),
352 # h.form(url('admin_setting', setting_id=ID),
355 # method='delete')
353 # method='delete')
356 # url('admin_setting', setting_id=ID)
354 # url('admin_setting', setting_id=ID)
357 if setting_id == 'hooks':
355 if setting_id == 'hooks':
358 hook_id = request.POST.get('hook_id')
356 hook_id = request.POST.get('hook_id')
359 RhodeCodeUi.delete(hook_id)
357 RhodeCodeUi.delete(hook_id)
360 Session().commit()
358 Session().commit()
361
359
362 @HasPermissionAllDecorator('hg.admin')
360 @HasPermissionAllDecorator('hg.admin')
363 def show(self, setting_id, format='html'):
361 def show(self, setting_id, format='html'):
364 """
362 """
365 GET /admin/settings/setting_id: Show a specific item"""
363 GET /admin/settings/setting_id: Show a specific item"""
366 # url('admin_setting', setting_id=ID)
364 # url('admin_setting', setting_id=ID)
367
365
368 @HasPermissionAllDecorator('hg.admin')
366 @HasPermissionAllDecorator('hg.admin')
369 def edit(self, setting_id, format='html'):
367 def edit(self, setting_id, format='html'):
370 """
368 """
371 GET /admin/settings/setting_id/edit: Form to
369 GET /admin/settings/setting_id/edit: Form to
372 edit an existing item"""
370 edit an existing item"""
373 # url('admin_edit_setting', setting_id=ID)
371 # url('admin_edit_setting', setting_id=ID)
374
372
375 c.hooks = RhodeCodeUi.get_builtin_hooks()
373 c.hooks = RhodeCodeUi.get_builtin_hooks()
376 c.custom_hooks = RhodeCodeUi.get_custom_hooks()
374 c.custom_hooks = RhodeCodeUi.get_custom_hooks()
377
375
378 return htmlfill.render(
376 return htmlfill.render(
379 render('admin/settings/hooks.html'),
377 render('admin/settings/hooks.html'),
380 defaults={},
378 defaults={},
381 encoding="UTF-8",
379 encoding="UTF-8",
382 force_defaults=False
380 force_defaults=False
383 )
381 )
384
382
385 def _load_my_repos_data(self):
383 def _load_my_repos_data(self):
386 repos_list = Session().query(Repository)\
384 repos_list = Session().query(Repository)\
387 .filter(Repository.user_id ==
385 .filter(Repository.user_id ==
388 self.rhodecode_user.user_id)\
386 self.rhodecode_user.user_id)\
389 .order_by(func.lower(Repository.repo_name)).all()
387 .order_by(func.lower(Repository.repo_name)).all()
390
388
391 repos_data = RepoModel().get_repos_as_dict(repos_list=repos_list,
389 repos_data = RepoModel().get_repos_as_dict(repos_list=repos_list,
392 admin=True)
390 admin=True)
393 #json used to render the grid
391 #json used to render the grid
394 return json.dumps(repos_data)
392 return json.dumps(repos_data)
395
393
396 @NotAnonymous()
394 @NotAnonymous()
397 def my_account(self):
395 def my_account(self):
398 """
396 """
399 GET /_admin/my_account Displays info about my account
397 GET /_admin/my_account Displays info about my account
400 """
398 """
401 # url('admin_settings_my_account')
399 # url('admin_settings_my_account')
402
400
403 c.user = User.get(self.rhodecode_user.user_id)
401 c.user = User.get(self.rhodecode_user.user_id)
404 c.perm_user = AuthUser(user_id=self.rhodecode_user.user_id,
402 c.perm_user = AuthUser(user_id=self.rhodecode_user.user_id,
405 ip_addr=self.ip_addr)
403 ip_addr=self.ip_addr)
406 c.ldap_dn = c.user.ldap_dn
404 c.ldap_dn = c.user.ldap_dn
407
405
408 if c.user.username == 'default':
406 if c.user.username == 'default':
409 h.flash(_("You can't edit this user since it's"
407 h.flash(_("You can't edit this user since it's"
410 " crucial for entire application"), category='warning')
408 " crucial for entire application"), category='warning')
411 return redirect(url('users'))
409 return redirect(url('users'))
412
410
413 #json used to render the grid
411 #json used to render the grid
414 c.data = self._load_my_repos_data()
412 c.data = self._load_my_repos_data()
415
413
416 defaults = c.user.get_dict()
414 defaults = c.user.get_dict()
417
415
418 c.form = htmlfill.render(
416 c.form = htmlfill.render(
419 render('admin/users/user_edit_my_account_form.html'),
417 render('admin/users/user_edit_my_account_form.html'),
420 defaults=defaults,
418 defaults=defaults,
421 encoding="UTF-8",
419 encoding="UTF-8",
422 force_defaults=False
420 force_defaults=False
423 )
421 )
424 return render('admin/users/user_edit_my_account.html')
422 return render('admin/users/user_edit_my_account.html')
425
423
426 @NotAnonymous()
424 @NotAnonymous()
427 def my_account_update(self):
425 def my_account_update(self):
428 """PUT /_admin/my_account_update: Update an existing item"""
426 """PUT /_admin/my_account_update: Update an existing item"""
429 # Forms posted to this method should contain a hidden field:
427 # Forms posted to this method should contain a hidden field:
430 # <input type="hidden" name="_method" value="PUT" />
428 # <input type="hidden" name="_method" value="PUT" />
431 # Or using helpers:
429 # Or using helpers:
432 # h.form(url('admin_settings_my_account_update'),
430 # h.form(url('admin_settings_my_account_update'),
433 # method='put')
431 # method='put')
434 # url('admin_settings_my_account_update', id=ID)
432 # url('admin_settings_my_account_update', id=ID)
435 uid = self.rhodecode_user.user_id
433 uid = self.rhodecode_user.user_id
436 c.user = User.get(self.rhodecode_user.user_id)
434 c.user = User.get(self.rhodecode_user.user_id)
437 c.perm_user = AuthUser(user_id=self.rhodecode_user.user_id,
435 c.perm_user = AuthUser(user_id=self.rhodecode_user.user_id,
438 ip_addr=self.ip_addr)
436 ip_addr=self.ip_addr)
439 c.ldap_dn = c.user.ldap_dn
437 c.ldap_dn = c.user.ldap_dn
440 email = self.rhodecode_user.email
438 email = self.rhodecode_user.email
441 _form = UserForm(edit=True,
439 _form = UserForm(edit=True,
442 old_data={'user_id': uid, 'email': email})()
440 old_data={'user_id': uid, 'email': email})()
443 form_result = {}
441 form_result = {}
444 try:
442 try:
445 form_result = _form.to_python(dict(request.POST))
443 form_result = _form.to_python(dict(request.POST))
446 skip_attrs = ['admin', 'active'] # skip attr for my account
444 skip_attrs = ['admin', 'active'] # skip attr for my account
447 if c.ldap_dn:
445 if c.ldap_dn:
448 #forbid updating username for ldap accounts
446 #forbid updating username for ldap accounts
449 skip_attrs.append('username')
447 skip_attrs.append('username')
450 UserModel().update(uid, form_result, skip_attrs=skip_attrs)
448 UserModel().update(uid, form_result, skip_attrs=skip_attrs)
451 h.flash(_('Your account was updated successfully'),
449 h.flash(_('Your account was updated successfully'),
452 category='success')
450 category='success')
453 Session().commit()
451 Session().commit()
454 except formencode.Invalid, errors:
452 except formencode.Invalid, errors:
455 #json used to render the grid
453 #json used to render the grid
456 c.data = self._load_my_repos_data()
454 c.data = self._load_my_repos_data()
457 c.form = htmlfill.render(
455 c.form = htmlfill.render(
458 render('admin/users/user_edit_my_account_form.html'),
456 render('admin/users/user_edit_my_account_form.html'),
459 defaults=errors.value,
457 defaults=errors.value,
460 errors=errors.error_dict or {},
458 errors=errors.error_dict or {},
461 prefix_error=False,
459 prefix_error=False,
462 encoding="UTF-8")
460 encoding="UTF-8")
463 return render('admin/users/user_edit_my_account.html')
461 return render('admin/users/user_edit_my_account.html')
464 except Exception:
462 except Exception:
465 log.error(traceback.format_exc())
463 log.error(traceback.format_exc())
466 h.flash(_('Error occurred during update of user %s') \
464 h.flash(_('Error occurred during update of user %s') \
467 % form_result.get('username'), category='error')
465 % form_result.get('username'), category='error')
468
466
469 return redirect(url('my_account'))
467 return redirect(url('my_account'))
470
468
471 @NotAnonymous()
469 @NotAnonymous()
472 def my_account_my_pullrequests(self):
470 def my_account_my_pullrequests(self):
473 c.show_closed = request.GET.get('pr_show_closed')
471 c.show_closed = request.GET.get('pr_show_closed')
474
472
475 def _filter(pr):
473 def _filter(pr):
476 s = sorted(pr, key=lambda o: o.created_on, reverse=True)
474 s = sorted(pr, key=lambda o: o.created_on, reverse=True)
477 if not c.show_closed:
475 if not c.show_closed:
478 s = filter(lambda p: p.status != PullRequest.STATUS_CLOSED, s)
476 s = filter(lambda p: p.status != PullRequest.STATUS_CLOSED, s)
479 return s
477 return s
480
478
481 c.my_pull_requests = _filter(PullRequest.query()\
479 c.my_pull_requests = _filter(PullRequest.query()\
482 .filter(PullRequest.user_id ==
480 .filter(PullRequest.user_id ==
483 self.rhodecode_user.user_id)\
481 self.rhodecode_user.user_id)\
484 .all())
482 .all())
485
483
486 c.participate_in_pull_requests = _filter([
484 c.participate_in_pull_requests = _filter([
487 x.pull_request for x in PullRequestReviewers.query()\
485 x.pull_request for x in PullRequestReviewers.query()\
488 .filter(PullRequestReviewers.user_id ==
486 .filter(PullRequestReviewers.user_id ==
489 self.rhodecode_user.user_id).all()])
487 self.rhodecode_user.user_id).all()])
490
488
491 return render('admin/users/user_edit_my_account_pullrequests.html')
489 return render('admin/users/user_edit_my_account_pullrequests.html')
492
490
493 def _get_hg_ui_settings(self):
491 def _get_hg_ui_settings(self):
494 ret = RhodeCodeUi.query().all()
492 ret = RhodeCodeUi.query().all()
495
493
496 if not ret:
494 if not ret:
497 raise Exception('Could not get application ui settings !')
495 raise Exception('Could not get application ui settings !')
498 settings = {}
496 settings = {}
499 for each in ret:
497 for each in ret:
500 k = each.ui_key
498 k = each.ui_key
501 v = each.ui_value
499 v = each.ui_value
502 if k == '/':
500 if k == '/':
503 k = 'root_path'
501 k = 'root_path'
504
502
505 if k == 'push_ssl':
503 if k == 'push_ssl':
506 v = str2bool(v)
504 v = str2bool(v)
507
505
508 if k.find('.') != -1:
506 if k.find('.') != -1:
509 k = k.replace('.', '_')
507 k = k.replace('.', '_')
510
508
511 if each.ui_section in ['hooks', 'extensions']:
509 if each.ui_section in ['hooks', 'extensions']:
512 v = each.ui_active
510 v = each.ui_active
513
511
514 settings[each.ui_section + '_' + k] = v
512 settings[each.ui_section + '_' + k] = v
515 return settings
513 return settings
@@ -1,362 +1,360 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.users
3 rhodecode.controllers.admin.users
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Users crud controller for pylons
6 Users crud controller for pylons
7
7
8 :created_on: Apr 4, 2010
8 :created_on: Apr 4, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28 import formencode
28 import formencode
29 from pylons import response
29 from pylons import response
30
30
31 from formencode import htmlfill
31 from formencode import htmlfill
32 from pylons import request, session, tmpl_context as c, url, config
32 from pylons import request, session, tmpl_context as c, url, config
33 from pylons.controllers.util import redirect
33 from pylons.controllers.util import redirect
34 from pylons.i18n.translation import _
34 from pylons.i18n.translation import _
35
35
36 import rhodecode
36 import rhodecode
37 from rhodecode.lib.exceptions import DefaultUserException, \
37 from rhodecode.lib.exceptions import DefaultUserException, \
38 UserOwnsReposException
38 UserOwnsReposException
39 from rhodecode.lib import helpers as h
39 from rhodecode.lib import helpers as h
40 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
40 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
41 AuthUser
41 AuthUser
42 from rhodecode.lib.base import BaseController, render
42 from rhodecode.lib.base import BaseController, render
43
43
44 from rhodecode.model.db import User, UserEmailMap, UserIpMap
44 from rhodecode.model.db import User, UserEmailMap, UserIpMap
45 from rhodecode.model.forms import UserForm
45 from rhodecode.model.forms import UserForm
46 from rhodecode.model.user import UserModel
46 from rhodecode.model.user import UserModel
47 from rhodecode.model.meta import Session
47 from rhodecode.model.meta import Session
48 from rhodecode.lib.utils import action_logger
48 from rhodecode.lib.utils import action_logger
49 from rhodecode.lib.compat import json
49 from rhodecode.lib.compat import json
50 from rhodecode.lib.utils2 import datetime_to_time, str2bool
50 from rhodecode.lib.utils2 import datetime_to_time, str2bool
51
51
52 log = logging.getLogger(__name__)
52 log = logging.getLogger(__name__)
53
53
54
54
55 class UsersController(BaseController):
55 class UsersController(BaseController):
56 """REST Controller styled on the Atom Publishing Protocol"""
56 """REST Controller styled on the Atom Publishing Protocol"""
57 # To properly map this controller, ensure your config/routing.py
57 # To properly map this controller, ensure your config/routing.py
58 # file has a resource setup:
58 # file has a resource setup:
59 # map.resource('user', 'users')
59 # map.resource('user', 'users')
60
60
61 @LoginRequired()
61 @LoginRequired()
62 @HasPermissionAllDecorator('hg.admin')
62 @HasPermissionAllDecorator('hg.admin')
63 def __before__(self):
63 def __before__(self):
64 c.admin_user = session.get('admin_user')
65 c.admin_username = session.get('admin_username')
66 super(UsersController, self).__before__()
64 super(UsersController, self).__before__()
67 c.available_permissions = config['available_permissions']
65 c.available_permissions = config['available_permissions']
68
66
69 def index(self, format='html'):
67 def index(self, format='html'):
70 """GET /users: All items in the collection"""
68 """GET /users: All items in the collection"""
71 # url('users')
69 # url('users')
72
70
73 c.users_list = User.query().order_by(User.username).all()
71 c.users_list = User.query().order_by(User.username).all()
74
72
75 users_data = []
73 users_data = []
76 total_records = len(c.users_list)
74 total_records = len(c.users_list)
77 _tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
75 _tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
78 template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
76 template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
79
77
80 grav_tmpl = lambda user_email, size: (
78 grav_tmpl = lambda user_email, size: (
81 template.get_def("user_gravatar")
79 template.get_def("user_gravatar")
82 .render(user_email, size, _=_, h=h, c=c))
80 .render(user_email, size, _=_, h=h, c=c))
83
81
84 user_lnk = lambda user_id, username: (
82 user_lnk = lambda user_id, username: (
85 template.get_def("user_name")
83 template.get_def("user_name")
86 .render(user_id, username, _=_, h=h, c=c))
84 .render(user_id, username, _=_, h=h, c=c))
87
85
88 user_actions = lambda user_id, username: (
86 user_actions = lambda user_id, username: (
89 template.get_def("user_actions")
87 template.get_def("user_actions")
90 .render(user_id, username, _=_, h=h, c=c))
88 .render(user_id, username, _=_, h=h, c=c))
91
89
92 for user in c.users_list:
90 for user in c.users_list:
93
91
94 users_data.append({
92 users_data.append({
95 "gravatar": grav_tmpl(user. email, 24),
93 "gravatar": grav_tmpl(user. email, 24),
96 "raw_username": user.username,
94 "raw_username": user.username,
97 "username": user_lnk(user.user_id, user.username),
95 "username": user_lnk(user.user_id, user.username),
98 "firstname": user.name,
96 "firstname": user.name,
99 "lastname": user.lastname,
97 "lastname": user.lastname,
100 "last_login": h.fmt_date(user.last_login),
98 "last_login": h.fmt_date(user.last_login),
101 "last_login_raw": datetime_to_time(user.last_login),
99 "last_login_raw": datetime_to_time(user.last_login),
102 "active": h.boolicon(user.active),
100 "active": h.boolicon(user.active),
103 "admin": h.boolicon(user.admin),
101 "admin": h.boolicon(user.admin),
104 "ldap": h.boolicon(bool(user.ldap_dn)),
102 "ldap": h.boolicon(bool(user.ldap_dn)),
105 "action": user_actions(user.user_id, user.username),
103 "action": user_actions(user.user_id, user.username),
106 })
104 })
107
105
108 c.data = json.dumps({
106 c.data = json.dumps({
109 "totalRecords": total_records,
107 "totalRecords": total_records,
110 "startIndex": 0,
108 "startIndex": 0,
111 "sort": None,
109 "sort": None,
112 "dir": "asc",
110 "dir": "asc",
113 "records": users_data
111 "records": users_data
114 })
112 })
115
113
116 return render('admin/users/users.html')
114 return render('admin/users/users.html')
117
115
118 def create(self):
116 def create(self):
119 """POST /users: Create a new item"""
117 """POST /users: Create a new item"""
120 # url('users')
118 # url('users')
121
119
122 user_model = UserModel()
120 user_model = UserModel()
123 user_form = UserForm()()
121 user_form = UserForm()()
124 try:
122 try:
125 form_result = user_form.to_python(dict(request.POST))
123 form_result = user_form.to_python(dict(request.POST))
126 user_model.create(form_result)
124 user_model.create(form_result)
127 usr = form_result['username']
125 usr = form_result['username']
128 action_logger(self.rhodecode_user, 'admin_created_user:%s' % usr,
126 action_logger(self.rhodecode_user, 'admin_created_user:%s' % usr,
129 None, self.ip_addr, self.sa)
127 None, self.ip_addr, self.sa)
130 h.flash(_('Created user %s') % usr,
128 h.flash(_('Created user %s') % usr,
131 category='success')
129 category='success')
132 Session().commit()
130 Session().commit()
133 except formencode.Invalid, errors:
131 except formencode.Invalid, errors:
134 return htmlfill.render(
132 return htmlfill.render(
135 render('admin/users/user_add.html'),
133 render('admin/users/user_add.html'),
136 defaults=errors.value,
134 defaults=errors.value,
137 errors=errors.error_dict or {},
135 errors=errors.error_dict or {},
138 prefix_error=False,
136 prefix_error=False,
139 encoding="UTF-8")
137 encoding="UTF-8")
140 except Exception:
138 except Exception:
141 log.error(traceback.format_exc())
139 log.error(traceback.format_exc())
142 h.flash(_('Error occurred during creation of user %s') \
140 h.flash(_('Error occurred during creation of user %s') \
143 % request.POST.get('username'), category='error')
141 % request.POST.get('username'), category='error')
144 return redirect(url('users'))
142 return redirect(url('users'))
145
143
146 def new(self, format='html'):
144 def new(self, format='html'):
147 """GET /users/new: Form to create a new item"""
145 """GET /users/new: Form to create a new item"""
148 # url('new_user')
146 # url('new_user')
149 return render('admin/users/user_add.html')
147 return render('admin/users/user_add.html')
150
148
151 def update(self, id):
149 def update(self, id):
152 """PUT /users/id: Update an existing item"""
150 """PUT /users/id: Update an existing item"""
153 # Forms posted to this method should contain a hidden field:
151 # Forms posted to this method should contain a hidden field:
154 # <input type="hidden" name="_method" value="PUT" />
152 # <input type="hidden" name="_method" value="PUT" />
155 # Or using helpers:
153 # Or using helpers:
156 # h.form(url('update_user', id=ID),
154 # h.form(url('update_user', id=ID),
157 # method='put')
155 # method='put')
158 # url('user', id=ID)
156 # url('user', id=ID)
159 user_model = UserModel()
157 user_model = UserModel()
160 c.user = user_model.get(id)
158 c.user = user_model.get(id)
161 c.ldap_dn = c.user.ldap_dn
159 c.ldap_dn = c.user.ldap_dn
162 c.perm_user = AuthUser(user_id=id, ip_addr=self.ip_addr)
160 c.perm_user = AuthUser(user_id=id, ip_addr=self.ip_addr)
163 _form = UserForm(edit=True, old_data={'user_id': id,
161 _form = UserForm(edit=True, old_data={'user_id': id,
164 'email': c.user.email})()
162 'email': c.user.email})()
165 form_result = {}
163 form_result = {}
166 try:
164 try:
167 form_result = _form.to_python(dict(request.POST))
165 form_result = _form.to_python(dict(request.POST))
168 skip_attrs = []
166 skip_attrs = []
169 if c.ldap_dn:
167 if c.ldap_dn:
170 #forbid updating username for ldap accounts
168 #forbid updating username for ldap accounts
171 skip_attrs = ['username']
169 skip_attrs = ['username']
172 user_model.update(id, form_result, skip_attrs=skip_attrs)
170 user_model.update(id, form_result, skip_attrs=skip_attrs)
173 usr = form_result['username']
171 usr = form_result['username']
174 action_logger(self.rhodecode_user, 'admin_updated_user:%s' % usr,
172 action_logger(self.rhodecode_user, 'admin_updated_user:%s' % usr,
175 None, self.ip_addr, self.sa)
173 None, self.ip_addr, self.sa)
176 h.flash(_('User updated successfully'), category='success')
174 h.flash(_('User updated successfully'), category='success')
177 Session().commit()
175 Session().commit()
178 except formencode.Invalid, errors:
176 except formencode.Invalid, errors:
179 c.user_email_map = UserEmailMap.query()\
177 c.user_email_map = UserEmailMap.query()\
180 .filter(UserEmailMap.user == c.user).all()
178 .filter(UserEmailMap.user == c.user).all()
181 c.user_ip_map = UserIpMap.query()\
179 c.user_ip_map = UserIpMap.query()\
182 .filter(UserIpMap.user == c.user).all()
180 .filter(UserIpMap.user == c.user).all()
183 defaults = errors.value
181 defaults = errors.value
184 e = errors.error_dict or {}
182 e = errors.error_dict or {}
185 defaults.update({
183 defaults.update({
186 'create_repo_perm': user_model.has_perm(id, 'hg.create.repository'),
184 'create_repo_perm': user_model.has_perm(id, 'hg.create.repository'),
187 'fork_repo_perm': user_model.has_perm(id, 'hg.fork.repository'),
185 'fork_repo_perm': user_model.has_perm(id, 'hg.fork.repository'),
188 '_method': 'put'
186 '_method': 'put'
189 })
187 })
190 return htmlfill.render(
188 return htmlfill.render(
191 render('admin/users/user_edit.html'),
189 render('admin/users/user_edit.html'),
192 defaults=defaults,
190 defaults=defaults,
193 errors=e,
191 errors=e,
194 prefix_error=False,
192 prefix_error=False,
195 encoding="UTF-8")
193 encoding="UTF-8")
196 except Exception:
194 except Exception:
197 log.error(traceback.format_exc())
195 log.error(traceback.format_exc())
198 h.flash(_('Error occurred during update of user %s') \
196 h.flash(_('Error occurred during update of user %s') \
199 % form_result.get('username'), category='error')
197 % form_result.get('username'), category='error')
200 return redirect(url('edit_user', id=id))
198 return redirect(url('edit_user', id=id))
201
199
202 def delete(self, id):
200 def delete(self, id):
203 """DELETE /users/id: Delete an existing item"""
201 """DELETE /users/id: Delete an existing item"""
204 # Forms posted to this method should contain a hidden field:
202 # Forms posted to this method should contain a hidden field:
205 # <input type="hidden" name="_method" value="DELETE" />
203 # <input type="hidden" name="_method" value="DELETE" />
206 # Or using helpers:
204 # Or using helpers:
207 # h.form(url('delete_user', id=ID),
205 # h.form(url('delete_user', id=ID),
208 # method='delete')
206 # method='delete')
209 # url('user', id=ID)
207 # url('user', id=ID)
210 usr = User.get_or_404(id)
208 usr = User.get_or_404(id)
211 try:
209 try:
212 UserModel().delete(usr)
210 UserModel().delete(usr)
213 Session().commit()
211 Session().commit()
214 h.flash(_('Successfully deleted user'), category='success')
212 h.flash(_('Successfully deleted user'), category='success')
215 except (UserOwnsReposException, DefaultUserException), e:
213 except (UserOwnsReposException, DefaultUserException), e:
216 h.flash(e, category='warning')
214 h.flash(e, category='warning')
217 except Exception:
215 except Exception:
218 log.error(traceback.format_exc())
216 log.error(traceback.format_exc())
219 h.flash(_('An error occurred during deletion of user'),
217 h.flash(_('An error occurred during deletion of user'),
220 category='error')
218 category='error')
221 return redirect(url('users'))
219 return redirect(url('users'))
222
220
223 def show(self, id, format='html'):
221 def show(self, id, format='html'):
224 """GET /users/id: Show a specific item"""
222 """GET /users/id: Show a specific item"""
225 # url('user', id=ID)
223 # url('user', id=ID)
226
224
227 def edit(self, id, format='html'):
225 def edit(self, id, format='html'):
228 """GET /users/id/edit: Form to edit an existing item"""
226 """GET /users/id/edit: Form to edit an existing item"""
229 # url('edit_user', id=ID)
227 # url('edit_user', id=ID)
230 c.user = User.get_or_404(id)
228 c.user = User.get_or_404(id)
231
229
232 if c.user.username == 'default':
230 if c.user.username == 'default':
233 h.flash(_("You can't edit this user"), category='warning')
231 h.flash(_("You can't edit this user"), category='warning')
234 return redirect(url('users'))
232 return redirect(url('users'))
235
233
236 c.perm_user = AuthUser(user_id=id, ip_addr=self.ip_addr)
234 c.perm_user = AuthUser(user_id=id, ip_addr=self.ip_addr)
237 c.user.permissions = {}
235 c.user.permissions = {}
238 c.granted_permissions = UserModel().fill_perms(c.user)\
236 c.granted_permissions = UserModel().fill_perms(c.user)\
239 .permissions['global']
237 .permissions['global']
240 c.user_email_map = UserEmailMap.query()\
238 c.user_email_map = UserEmailMap.query()\
241 .filter(UserEmailMap.user == c.user).all()
239 .filter(UserEmailMap.user == c.user).all()
242 c.user_ip_map = UserIpMap.query()\
240 c.user_ip_map = UserIpMap.query()\
243 .filter(UserIpMap.user == c.user).all()
241 .filter(UserIpMap.user == c.user).all()
244 user_model = UserModel()
242 user_model = UserModel()
245 c.ldap_dn = c.user.ldap_dn
243 c.ldap_dn = c.user.ldap_dn
246 defaults = c.user.get_dict()
244 defaults = c.user.get_dict()
247 defaults.update({
245 defaults.update({
248 'create_repo_perm': user_model.has_perm(id, 'hg.create.repository'),
246 'create_repo_perm': user_model.has_perm(id, 'hg.create.repository'),
249 'fork_repo_perm': user_model.has_perm(id, 'hg.fork.repository'),
247 'fork_repo_perm': user_model.has_perm(id, 'hg.fork.repository'),
250 })
248 })
251
249
252 return htmlfill.render(
250 return htmlfill.render(
253 render('admin/users/user_edit.html'),
251 render('admin/users/user_edit.html'),
254 defaults=defaults,
252 defaults=defaults,
255 encoding="UTF-8",
253 encoding="UTF-8",
256 force_defaults=False
254 force_defaults=False
257 )
255 )
258
256
259 def update_perm(self, id):
257 def update_perm(self, id):
260 """PUT /users_perm/id: Update an existing item"""
258 """PUT /users_perm/id: Update an existing item"""
261 # url('user_perm', id=ID, method='put')
259 # url('user_perm', id=ID, method='put')
262 usr = User.get_or_404(id)
260 usr = User.get_or_404(id)
263 grant_create_perm = str2bool(request.POST.get('create_repo_perm'))
261 grant_create_perm = str2bool(request.POST.get('create_repo_perm'))
264 grant_fork_perm = str2bool(request.POST.get('fork_repo_perm'))
262 grant_fork_perm = str2bool(request.POST.get('fork_repo_perm'))
265 inherit_perms = str2bool(request.POST.get('inherit_default_permissions'))
263 inherit_perms = str2bool(request.POST.get('inherit_default_permissions'))
266
264
267 user_model = UserModel()
265 user_model = UserModel()
268
266
269 try:
267 try:
270 usr.inherit_default_permissions = inherit_perms
268 usr.inherit_default_permissions = inherit_perms
271 Session().add(usr)
269 Session().add(usr)
272
270
273 if grant_create_perm:
271 if grant_create_perm:
274 user_model.revoke_perm(usr, 'hg.create.none')
272 user_model.revoke_perm(usr, 'hg.create.none')
275 user_model.grant_perm(usr, 'hg.create.repository')
273 user_model.grant_perm(usr, 'hg.create.repository')
276 h.flash(_("Granted 'repository create' permission to user"),
274 h.flash(_("Granted 'repository create' permission to user"),
277 category='success')
275 category='success')
278 else:
276 else:
279 user_model.revoke_perm(usr, 'hg.create.repository')
277 user_model.revoke_perm(usr, 'hg.create.repository')
280 user_model.grant_perm(usr, 'hg.create.none')
278 user_model.grant_perm(usr, 'hg.create.none')
281 h.flash(_("Revoked 'repository create' permission to user"),
279 h.flash(_("Revoked 'repository create' permission to user"),
282 category='success')
280 category='success')
283
281
284 if grant_fork_perm:
282 if grant_fork_perm:
285 user_model.revoke_perm(usr, 'hg.fork.none')
283 user_model.revoke_perm(usr, 'hg.fork.none')
286 user_model.grant_perm(usr, 'hg.fork.repository')
284 user_model.grant_perm(usr, 'hg.fork.repository')
287 h.flash(_("Granted 'repository fork' permission to user"),
285 h.flash(_("Granted 'repository fork' permission to user"),
288 category='success')
286 category='success')
289 else:
287 else:
290 user_model.revoke_perm(usr, 'hg.fork.repository')
288 user_model.revoke_perm(usr, 'hg.fork.repository')
291 user_model.grant_perm(usr, 'hg.fork.none')
289 user_model.grant_perm(usr, 'hg.fork.none')
292 h.flash(_("Revoked 'repository fork' permission to user"),
290 h.flash(_("Revoked 'repository fork' permission to user"),
293 category='success')
291 category='success')
294
292
295 Session().commit()
293 Session().commit()
296 except Exception:
294 except Exception:
297 log.error(traceback.format_exc())
295 log.error(traceback.format_exc())
298 h.flash(_('An error occurred during permissions saving'),
296 h.flash(_('An error occurred during permissions saving'),
299 category='error')
297 category='error')
300 return redirect(url('edit_user', id=id))
298 return redirect(url('edit_user', id=id))
301
299
302 def add_email(self, id):
300 def add_email(self, id):
303 """POST /user_emails:Add an existing item"""
301 """POST /user_emails:Add an existing item"""
304 # url('user_emails', id=ID, method='put')
302 # url('user_emails', id=ID, method='put')
305
303
306 email = request.POST.get('new_email')
304 email = request.POST.get('new_email')
307 user_model = UserModel()
305 user_model = UserModel()
308
306
309 try:
307 try:
310 user_model.add_extra_email(id, email)
308 user_model.add_extra_email(id, email)
311 Session().commit()
309 Session().commit()
312 h.flash(_("Added email %s to user") % email, category='success')
310 h.flash(_("Added email %s to user") % email, category='success')
313 except formencode.Invalid, error:
311 except formencode.Invalid, error:
314 msg = error.error_dict['email']
312 msg = error.error_dict['email']
315 h.flash(msg, category='error')
313 h.flash(msg, category='error')
316 except Exception:
314 except Exception:
317 log.error(traceback.format_exc())
315 log.error(traceback.format_exc())
318 h.flash(_('An error occurred during email saving'),
316 h.flash(_('An error occurred during email saving'),
319 category='error')
317 category='error')
320 return redirect(url('edit_user', id=id))
318 return redirect(url('edit_user', id=id))
321
319
322 def delete_email(self, id):
320 def delete_email(self, id):
323 """DELETE /user_emails_delete/id: Delete an existing item"""
321 """DELETE /user_emails_delete/id: Delete an existing item"""
324 # url('user_emails_delete', id=ID, method='delete')
322 # url('user_emails_delete', id=ID, method='delete')
325 user_model = UserModel()
323 user_model = UserModel()
326 user_model.delete_extra_email(id, request.POST.get('del_email'))
324 user_model.delete_extra_email(id, request.POST.get('del_email'))
327 Session().commit()
325 Session().commit()
328 h.flash(_("Removed email from user"), category='success')
326 h.flash(_("Removed email from user"), category='success')
329 return redirect(url('edit_user', id=id))
327 return redirect(url('edit_user', id=id))
330
328
331 def add_ip(self, id):
329 def add_ip(self, id):
332 """POST /user_ips:Add an existing item"""
330 """POST /user_ips:Add an existing item"""
333 # url('user_ips', id=ID, method='put')
331 # url('user_ips', id=ID, method='put')
334
332
335 ip = request.POST.get('new_ip')
333 ip = request.POST.get('new_ip')
336 user_model = UserModel()
334 user_model = UserModel()
337
335
338 try:
336 try:
339 user_model.add_extra_ip(id, ip)
337 user_model.add_extra_ip(id, ip)
340 Session().commit()
338 Session().commit()
341 h.flash(_("Added ip %s to user") % ip, category='success')
339 h.flash(_("Added ip %s to user") % ip, category='success')
342 except formencode.Invalid, error:
340 except formencode.Invalid, error:
343 msg = error.error_dict['ip']
341 msg = error.error_dict['ip']
344 h.flash(msg, category='error')
342 h.flash(msg, category='error')
345 except Exception:
343 except Exception:
346 log.error(traceback.format_exc())
344 log.error(traceback.format_exc())
347 h.flash(_('An error occurred during ip saving'),
345 h.flash(_('An error occurred during ip saving'),
348 category='error')
346 category='error')
349 if 'default_user' in request.POST:
347 if 'default_user' in request.POST:
350 return redirect(url('edit_permission', id='default'))
348 return redirect(url('edit_permission', id='default'))
351 return redirect(url('edit_user', id=id))
349 return redirect(url('edit_user', id=id))
352
350
353 def delete_ip(self, id):
351 def delete_ip(self, id):
354 """DELETE /user_ips_delete/id: Delete an existing item"""
352 """DELETE /user_ips_delete/id: Delete an existing item"""
355 # url('user_ips_delete', id=ID, method='delete')
353 # url('user_ips_delete', id=ID, method='delete')
356 user_model = UserModel()
354 user_model = UserModel()
357 user_model.delete_extra_ip(id, request.POST.get('del_ip'))
355 user_model.delete_extra_ip(id, request.POST.get('del_ip'))
358 Session().commit()
356 Session().commit()
359 h.flash(_("Removed ip from user"), category='success')
357 h.flash(_("Removed ip from user"), category='success')
360 if 'default_user' in request.POST:
358 if 'default_user' in request.POST:
361 return redirect(url('edit_permission', id='default'))
359 return redirect(url('edit_permission', id='default'))
362 return redirect(url('edit_user', id=id))
360 return redirect(url('edit_user', id=id))
@@ -1,284 +1,282 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.users_groups
3 rhodecode.controllers.admin.users_groups
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 User Groups crud controller for pylons
6 User Groups crud controller for pylons
7
7
8 :created_on: Jan 25, 2011
8 :created_on: Jan 25, 2011
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
29
30 from formencode import htmlfill
30 from formencode import htmlfill
31 from pylons import request, session, tmpl_context as c, url, config
31 from pylons import request, session, tmpl_context as c, url, config
32 from pylons.controllers.util import abort, redirect
32 from pylons.controllers.util import abort, redirect
33 from pylons.i18n.translation import _
33 from pylons.i18n.translation import _
34
34
35 from rhodecode.lib import helpers as h
35 from rhodecode.lib import helpers as h
36 from rhodecode.lib.exceptions import UserGroupsAssignedException
36 from rhodecode.lib.exceptions import UserGroupsAssignedException
37 from rhodecode.lib.utils2 import safe_unicode, str2bool
37 from rhodecode.lib.utils2 import safe_unicode, str2bool
38 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
38 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
39 from rhodecode.lib.base import BaseController, render
39 from rhodecode.lib.base import BaseController, render
40
40
41 from rhodecode.model.users_group import UserGroupModel
41 from rhodecode.model.users_group import UserGroupModel
42
42
43 from rhodecode.model.db import User, UserGroup, UserGroupToPerm,\
43 from rhodecode.model.db import User, UserGroup, UserGroupToPerm,\
44 UserGroupRepoToPerm, UserGroupRepoGroupToPerm
44 UserGroupRepoToPerm, UserGroupRepoGroupToPerm
45 from rhodecode.model.forms import UserGroupForm
45 from rhodecode.model.forms import UserGroupForm
46 from rhodecode.model.meta import Session
46 from rhodecode.model.meta import Session
47 from rhodecode.lib.utils import action_logger
47 from rhodecode.lib.utils import action_logger
48 from sqlalchemy.orm import joinedload
48 from sqlalchemy.orm import joinedload
49
49
50 log = logging.getLogger(__name__)
50 log = logging.getLogger(__name__)
51
51
52
52
53 class UsersGroupsController(BaseController):
53 class UsersGroupsController(BaseController):
54 """REST Controller styled on the Atom Publishing Protocol"""
54 """REST Controller styled on the Atom Publishing Protocol"""
55 # To properly map this controller, ensure your config/routing.py
55 # To properly map this controller, ensure your config/routing.py
56 # file has a resource setup:
56 # file has a resource setup:
57 # map.resource('users_group', 'users_groups')
57 # map.resource('users_group', 'users_groups')
58
58
59 @LoginRequired()
59 @LoginRequired()
60 @HasPermissionAllDecorator('hg.admin')
60 @HasPermissionAllDecorator('hg.admin')
61 def __before__(self):
61 def __before__(self):
62 c.admin_user = session.get('admin_user')
63 c.admin_username = session.get('admin_username')
64 super(UsersGroupsController, self).__before__()
62 super(UsersGroupsController, self).__before__()
65 c.available_permissions = config['available_permissions']
63 c.available_permissions = config['available_permissions']
66
64
67 def index(self, format='html'):
65 def index(self, format='html'):
68 """GET /users_groups: All items in the collection"""
66 """GET /users_groups: All items in the collection"""
69 # url('users_groups')
67 # url('users_groups')
70 c.users_groups_list = UserGroup().query().all()
68 c.users_groups_list = UserGroup().query().all()
71 return render('admin/users_groups/users_groups.html')
69 return render('admin/users_groups/users_groups.html')
72
70
73 def create(self):
71 def create(self):
74 """POST /users_groups: Create a new item"""
72 """POST /users_groups: Create a new item"""
75 # url('users_groups')
73 # url('users_groups')
76
74
77 users_group_form = UserGroupForm()()
75 users_group_form = UserGroupForm()()
78 try:
76 try:
79 form_result = users_group_form.to_python(dict(request.POST))
77 form_result = users_group_form.to_python(dict(request.POST))
80 UserGroupModel().create(name=form_result['users_group_name'],
78 UserGroupModel().create(name=form_result['users_group_name'],
81 active=form_result['users_group_active'])
79 active=form_result['users_group_active'])
82 gr = form_result['users_group_name']
80 gr = form_result['users_group_name']
83 action_logger(self.rhodecode_user,
81 action_logger(self.rhodecode_user,
84 'admin_created_users_group:%s' % gr,
82 'admin_created_users_group:%s' % gr,
85 None, self.ip_addr, self.sa)
83 None, self.ip_addr, self.sa)
86 h.flash(_('Created user group %s') % gr, category='success')
84 h.flash(_('Created user group %s') % gr, category='success')
87 Session().commit()
85 Session().commit()
88 except formencode.Invalid, errors:
86 except formencode.Invalid, errors:
89 return htmlfill.render(
87 return htmlfill.render(
90 render('admin/users_groups/users_group_add.html'),
88 render('admin/users_groups/users_group_add.html'),
91 defaults=errors.value,
89 defaults=errors.value,
92 errors=errors.error_dict or {},
90 errors=errors.error_dict or {},
93 prefix_error=False,
91 prefix_error=False,
94 encoding="UTF-8")
92 encoding="UTF-8")
95 except Exception:
93 except Exception:
96 log.error(traceback.format_exc())
94 log.error(traceback.format_exc())
97 h.flash(_('Error occurred during creation of user group %s') \
95 h.flash(_('Error occurred during creation of user group %s') \
98 % request.POST.get('users_group_name'), category='error')
96 % request.POST.get('users_group_name'), category='error')
99
97
100 return redirect(url('users_groups'))
98 return redirect(url('users_groups'))
101
99
102 def new(self, format='html'):
100 def new(self, format='html'):
103 """GET /users_groups/new: Form to create a new item"""
101 """GET /users_groups/new: Form to create a new item"""
104 # url('new_users_group')
102 # url('new_users_group')
105 return render('admin/users_groups/users_group_add.html')
103 return render('admin/users_groups/users_group_add.html')
106
104
107 def _load_data(self, id):
105 def _load_data(self, id):
108 c.users_group.permissions = {
106 c.users_group.permissions = {
109 'repositories': {},
107 'repositories': {},
110 'repositories_groups': {}
108 'repositories_groups': {}
111 }
109 }
112
110
113 ugroup_repo_perms = UserGroupRepoToPerm.query()\
111 ugroup_repo_perms = UserGroupRepoToPerm.query()\
114 .options(joinedload(UserGroupRepoToPerm.permission))\
112 .options(joinedload(UserGroupRepoToPerm.permission))\
115 .options(joinedload(UserGroupRepoToPerm.repository))\
113 .options(joinedload(UserGroupRepoToPerm.repository))\
116 .filter(UserGroupRepoToPerm.users_group_id == id)\
114 .filter(UserGroupRepoToPerm.users_group_id == id)\
117 .all()
115 .all()
118
116
119 for gr in ugroup_repo_perms:
117 for gr in ugroup_repo_perms:
120 c.users_group.permissions['repositories'][gr.repository.repo_name] \
118 c.users_group.permissions['repositories'][gr.repository.repo_name] \
121 = gr.permission.permission_name
119 = gr.permission.permission_name
122
120
123 ugroup_group_perms = UserGroupRepoGroupToPerm.query()\
121 ugroup_group_perms = UserGroupRepoGroupToPerm.query()\
124 .options(joinedload(UserGroupRepoGroupToPerm.permission))\
122 .options(joinedload(UserGroupRepoGroupToPerm.permission))\
125 .options(joinedload(UserGroupRepoGroupToPerm.group))\
123 .options(joinedload(UserGroupRepoGroupToPerm.group))\
126 .filter(UserGroupRepoGroupToPerm.users_group_id == id)\
124 .filter(UserGroupRepoGroupToPerm.users_group_id == id)\
127 .all()
125 .all()
128
126
129 for gr in ugroup_group_perms:
127 for gr in ugroup_group_perms:
130 c.users_group.permissions['repositories_groups'][gr.group.group_name] \
128 c.users_group.permissions['repositories_groups'][gr.group.group_name] \
131 = gr.permission.permission_name
129 = gr.permission.permission_name
132
130
133 c.group_members_obj = sorted((x.user for x in c.users_group.members),
131 c.group_members_obj = sorted((x.user for x in c.users_group.members),
134 key=lambda u: u.username.lower())
132 key=lambda u: u.username.lower())
135 c.group_members = [(x.user_id, x.username) for x in
133 c.group_members = [(x.user_id, x.username) for x in
136 c.group_members_obj]
134 c.group_members_obj]
137 c.available_members = sorted(((x.user_id, x.username) for x in
135 c.available_members = sorted(((x.user_id, x.username) for x in
138 User.query().all()),
136 User.query().all()),
139 key=lambda u: u[1].lower())
137 key=lambda u: u[1].lower())
140
138
141 def update(self, id):
139 def update(self, id):
142 """PUT /users_groups/id: Update an existing item"""
140 """PUT /users_groups/id: Update an existing item"""
143 # Forms posted to this method should contain a hidden field:
141 # Forms posted to this method should contain a hidden field:
144 # <input type="hidden" name="_method" value="PUT" />
142 # <input type="hidden" name="_method" value="PUT" />
145 # Or using helpers:
143 # Or using helpers:
146 # h.form(url('users_group', id=ID),
144 # h.form(url('users_group', id=ID),
147 # method='put')
145 # method='put')
148 # url('users_group', id=ID)
146 # url('users_group', id=ID)
149
147
150 c.users_group = UserGroup.get_or_404(id)
148 c.users_group = UserGroup.get_or_404(id)
151 self._load_data(id)
149 self._load_data(id)
152
150
153 available_members = [safe_unicode(x[0]) for x in c.available_members]
151 available_members = [safe_unicode(x[0]) for x in c.available_members]
154
152
155 users_group_form = UserGroupForm(edit=True,
153 users_group_form = UserGroupForm(edit=True,
156 old_data=c.users_group.get_dict(),
154 old_data=c.users_group.get_dict(),
157 available_members=available_members)()
155 available_members=available_members)()
158
156
159 try:
157 try:
160 form_result = users_group_form.to_python(request.POST)
158 form_result = users_group_form.to_python(request.POST)
161 UserGroupModel().update(c.users_group, form_result)
159 UserGroupModel().update(c.users_group, form_result)
162 gr = form_result['users_group_name']
160 gr = form_result['users_group_name']
163 action_logger(self.rhodecode_user,
161 action_logger(self.rhodecode_user,
164 'admin_updated_users_group:%s' % gr,
162 'admin_updated_users_group:%s' % gr,
165 None, self.ip_addr, self.sa)
163 None, self.ip_addr, self.sa)
166 h.flash(_('Updated user group %s') % gr, category='success')
164 h.flash(_('Updated user group %s') % gr, category='success')
167 Session().commit()
165 Session().commit()
168 except formencode.Invalid, errors:
166 except formencode.Invalid, errors:
169 ug_model = UserGroupModel()
167 ug_model = UserGroupModel()
170 defaults = errors.value
168 defaults = errors.value
171 e = errors.error_dict or {}
169 e = errors.error_dict or {}
172 defaults.update({
170 defaults.update({
173 'create_repo_perm': ug_model.has_perm(id,
171 'create_repo_perm': ug_model.has_perm(id,
174 'hg.create.repository'),
172 'hg.create.repository'),
175 'fork_repo_perm': ug_model.has_perm(id,
173 'fork_repo_perm': ug_model.has_perm(id,
176 'hg.fork.repository'),
174 'hg.fork.repository'),
177 '_method': 'put'
175 '_method': 'put'
178 })
176 })
179
177
180 return htmlfill.render(
178 return htmlfill.render(
181 render('admin/users_groups/users_group_edit.html'),
179 render('admin/users_groups/users_group_edit.html'),
182 defaults=defaults,
180 defaults=defaults,
183 errors=e,
181 errors=e,
184 prefix_error=False,
182 prefix_error=False,
185 encoding="UTF-8")
183 encoding="UTF-8")
186 except Exception:
184 except Exception:
187 log.error(traceback.format_exc())
185 log.error(traceback.format_exc())
188 h.flash(_('Error occurred during update of user group %s') \
186 h.flash(_('Error occurred during update of user group %s') \
189 % request.POST.get('users_group_name'), category='error')
187 % request.POST.get('users_group_name'), category='error')
190
188
191 return redirect(url('edit_users_group', id=id))
189 return redirect(url('edit_users_group', id=id))
192
190
193 def delete(self, id):
191 def delete(self, id):
194 """DELETE /users_groups/id: Delete an existing item"""
192 """DELETE /users_groups/id: Delete an existing item"""
195 # Forms posted to this method should contain a hidden field:
193 # Forms posted to this method should contain a hidden field:
196 # <input type="hidden" name="_method" value="DELETE" />
194 # <input type="hidden" name="_method" value="DELETE" />
197 # Or using helpers:
195 # Or using helpers:
198 # h.form(url('users_group', id=ID),
196 # h.form(url('users_group', id=ID),
199 # method='delete')
197 # method='delete')
200 # url('users_group', id=ID)
198 # url('users_group', id=ID)
201 usr_gr = UserGroup.get_or_404(id)
199 usr_gr = UserGroup.get_or_404(id)
202 try:
200 try:
203 UserGroupModel().delete(usr_gr)
201 UserGroupModel().delete(usr_gr)
204 Session().commit()
202 Session().commit()
205 h.flash(_('Successfully deleted user group'), category='success')
203 h.flash(_('Successfully deleted user group'), category='success')
206 except UserGroupsAssignedException, e:
204 except UserGroupsAssignedException, e:
207 h.flash(e, category='error')
205 h.flash(e, category='error')
208 except Exception:
206 except Exception:
209 log.error(traceback.format_exc())
207 log.error(traceback.format_exc())
210 h.flash(_('An error occurred during deletion of user group'),
208 h.flash(_('An error occurred during deletion of user group'),
211 category='error')
209 category='error')
212 return redirect(url('users_groups'))
210 return redirect(url('users_groups'))
213
211
214 def show(self, id, format='html'):
212 def show(self, id, format='html'):
215 """GET /users_groups/id: Show a specific item"""
213 """GET /users_groups/id: Show a specific item"""
216 # url('users_group', id=ID)
214 # url('users_group', id=ID)
217
215
218 def edit(self, id, format='html'):
216 def edit(self, id, format='html'):
219 """GET /users_groups/id/edit: Form to edit an existing item"""
217 """GET /users_groups/id/edit: Form to edit an existing item"""
220 # url('edit_users_group', id=ID)
218 # url('edit_users_group', id=ID)
221
219
222 c.users_group = UserGroup.get_or_404(id)
220 c.users_group = UserGroup.get_or_404(id)
223 self._load_data(id)
221 self._load_data(id)
224
222
225 ug_model = UserGroupModel()
223 ug_model = UserGroupModel()
226 defaults = c.users_group.get_dict()
224 defaults = c.users_group.get_dict()
227 defaults.update({
225 defaults.update({
228 'create_repo_perm': ug_model.has_perm(c.users_group,
226 'create_repo_perm': ug_model.has_perm(c.users_group,
229 'hg.create.repository'),
227 'hg.create.repository'),
230 'fork_repo_perm': ug_model.has_perm(c.users_group,
228 'fork_repo_perm': ug_model.has_perm(c.users_group,
231 'hg.fork.repository'),
229 'hg.fork.repository'),
232 })
230 })
233
231
234 return htmlfill.render(
232 return htmlfill.render(
235 render('admin/users_groups/users_group_edit.html'),
233 render('admin/users_groups/users_group_edit.html'),
236 defaults=defaults,
234 defaults=defaults,
237 encoding="UTF-8",
235 encoding="UTF-8",
238 force_defaults=False
236 force_defaults=False
239 )
237 )
240
238
241 def update_perm(self, id):
239 def update_perm(self, id):
242 """PUT /users_perm/id: Update an existing item"""
240 """PUT /users_perm/id: Update an existing item"""
243 # url('users_group_perm', id=ID, method='put')
241 # url('users_group_perm', id=ID, method='put')
244
242
245 users_group = UserGroup.get_or_404(id)
243 users_group = UserGroup.get_or_404(id)
246 grant_create_perm = str2bool(request.POST.get('create_repo_perm'))
244 grant_create_perm = str2bool(request.POST.get('create_repo_perm'))
247 grant_fork_perm = str2bool(request.POST.get('fork_repo_perm'))
245 grant_fork_perm = str2bool(request.POST.get('fork_repo_perm'))
248 inherit_perms = str2bool(request.POST.get('inherit_default_permissions'))
246 inherit_perms = str2bool(request.POST.get('inherit_default_permissions'))
249
247
250 usergroup_model = UserGroupModel()
248 usergroup_model = UserGroupModel()
251
249
252 try:
250 try:
253 users_group.inherit_default_permissions = inherit_perms
251 users_group.inherit_default_permissions = inherit_perms
254 Session().add(users_group)
252 Session().add(users_group)
255
253
256 if grant_create_perm:
254 if grant_create_perm:
257 usergroup_model.revoke_perm(id, 'hg.create.none')
255 usergroup_model.revoke_perm(id, 'hg.create.none')
258 usergroup_model.grant_perm(id, 'hg.create.repository')
256 usergroup_model.grant_perm(id, 'hg.create.repository')
259 h.flash(_("Granted 'repository create' permission to user group"),
257 h.flash(_("Granted 'repository create' permission to user group"),
260 category='success')
258 category='success')
261 else:
259 else:
262 usergroup_model.revoke_perm(id, 'hg.create.repository')
260 usergroup_model.revoke_perm(id, 'hg.create.repository')
263 usergroup_model.grant_perm(id, 'hg.create.none')
261 usergroup_model.grant_perm(id, 'hg.create.none')
264 h.flash(_("Revoked 'repository create' permission to user group"),
262 h.flash(_("Revoked 'repository create' permission to user group"),
265 category='success')
263 category='success')
266
264
267 if grant_fork_perm:
265 if grant_fork_perm:
268 usergroup_model.revoke_perm(id, 'hg.fork.none')
266 usergroup_model.revoke_perm(id, 'hg.fork.none')
269 usergroup_model.grant_perm(id, 'hg.fork.repository')
267 usergroup_model.grant_perm(id, 'hg.fork.repository')
270 h.flash(_("Granted 'repository fork' permission to user group"),
268 h.flash(_("Granted 'repository fork' permission to user group"),
271 category='success')
269 category='success')
272 else:
270 else:
273 usergroup_model.revoke_perm(id, 'hg.fork.repository')
271 usergroup_model.revoke_perm(id, 'hg.fork.repository')
274 usergroup_model.grant_perm(id, 'hg.fork.none')
272 usergroup_model.grant_perm(id, 'hg.fork.none')
275 h.flash(_("Revoked 'repository fork' permission to user group"),
273 h.flash(_("Revoked 'repository fork' permission to user group"),
276 category='success')
274 category='success')
277
275
278 Session().commit()
276 Session().commit()
279 except Exception:
277 except Exception:
280 log.error(traceback.format_exc())
278 log.error(traceback.format_exc())
281 h.flash(_('An error occurred during permissions saving'),
279 h.flash(_('An error occurred during permissions saving'),
282 category='error')
280 category='error')
283
281
284 return redirect(url('edit_users_group', id=id))
282 return redirect(url('edit_users_group', id=id))
@@ -1,2042 +1,2041 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.db_1_6_0
3 rhodecode.model.db_1_6_0
4 ~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Database Models for RhodeCode <=1.5.X
6 Database Models for RhodeCode <=1.5.X
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 from rhodecode.lib.vcs.backends.base import EmptyChangeset
47 from rhodecode.lib.vcs.backends.base import EmptyChangeset
48
48
49 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
49 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
50 safe_unicode, remove_suffix, remove_prefix, time_to_datetime, _set_extras
50 safe_unicode, remove_suffix, remove_prefix, time_to_datetime, _set_extras
51 from rhodecode.lib.compat import json
51 from rhodecode.lib.compat import json
52 from rhodecode.lib.caching_query import FromCache
52 from rhodecode.lib.caching_query import FromCache
53
53
54 from rhodecode.model.meta import Base, Session
54 from rhodecode.model.meta import Base, Session
55
55
56 URL_SEP = '/'
56 URL_SEP = '/'
57 log = logging.getLogger(__name__)
57 log = logging.getLogger(__name__)
58
58
59 #==============================================================================
59 #==============================================================================
60 # BASE CLASSES
60 # BASE CLASSES
61 #==============================================================================
61 #==============================================================================
62
62
63 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
63 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
64
64
65
65
66 class BaseModel(object):
66 class BaseModel(object):
67 """
67 """
68 Base Model for all classess
68 Base Model for all classess
69 """
69 """
70
70
71 @classmethod
71 @classmethod
72 def _get_keys(cls):
72 def _get_keys(cls):
73 """return column names for this model """
73 """return column names for this model """
74 return class_mapper(cls).c.keys()
74 return class_mapper(cls).c.keys()
75
75
76 def get_dict(self):
76 def get_dict(self):
77 """
77 """
78 return dict with keys and values corresponding
78 return dict with keys and values corresponding
79 to this model data """
79 to this model data """
80
80
81 d = {}
81 d = {}
82 for k in self._get_keys():
82 for k in self._get_keys():
83 d[k] = getattr(self, k)
83 d[k] = getattr(self, k)
84
84
85 # also use __json__() if present to get additional fields
85 # also use __json__() if present to get additional fields
86 _json_attr = getattr(self, '__json__', None)
86 _json_attr = getattr(self, '__json__', None)
87 if _json_attr:
87 if _json_attr:
88 # update with attributes from __json__
88 # update with attributes from __json__
89 if callable(_json_attr):
89 if callable(_json_attr):
90 _json_attr = _json_attr()
90 _json_attr = _json_attr()
91 for k, val in _json_attr.iteritems():
91 for k, val in _json_attr.iteritems():
92 d[k] = val
92 d[k] = val
93 return d
93 return d
94
94
95 def get_appstruct(self):
95 def get_appstruct(self):
96 """return list with keys and values tupples corresponding
96 """return list with keys and values tupples corresponding
97 to this model data """
97 to this model data """
98
98
99 l = []
99 l = []
100 for k in self._get_keys():
100 for k in self._get_keys():
101 l.append((k, getattr(self, k),))
101 l.append((k, getattr(self, k),))
102 return l
102 return l
103
103
104 def populate_obj(self, populate_dict):
104 def populate_obj(self, populate_dict):
105 """populate model with data from given populate_dict"""
105 """populate model with data from given populate_dict"""
106
106
107 for k in self._get_keys():
107 for k in self._get_keys():
108 if k in populate_dict:
108 if k in populate_dict:
109 setattr(self, k, populate_dict[k])
109 setattr(self, k, populate_dict[k])
110
110
111 @classmethod
111 @classmethod
112 def query(cls):
112 def query(cls):
113 return Session().query(cls)
113 return Session().query(cls)
114
114
115 @classmethod
115 @classmethod
116 def get(cls, id_):
116 def get(cls, id_):
117 if id_:
117 if id_:
118 return cls.query().get(id_)
118 return cls.query().get(id_)
119
119
120 @classmethod
120 @classmethod
121 def get_or_404(cls, id_):
121 def get_or_404(cls, id_):
122 try:
122 try:
123 id_ = int(id_)
123 id_ = int(id_)
124 except (TypeError, ValueError):
124 except (TypeError, ValueError):
125 raise HTTPNotFound
125 raise HTTPNotFound
126
126
127 res = cls.query().get(id_)
127 res = cls.query().get(id_)
128 if not res:
128 if not res:
129 raise HTTPNotFound
129 raise HTTPNotFound
130 return res
130 return res
131
131
132 @classmethod
132 @classmethod
133 def getAll(cls):
133 def getAll(cls):
134 return cls.query().all()
134 return cls.query().all()
135
135
136 @classmethod
136 @classmethod
137 def delete(cls, id_):
137 def delete(cls, id_):
138 obj = cls.query().get(id_)
138 obj = cls.query().get(id_)
139 Session().delete(obj)
139 Session().delete(obj)
140
140
141 def __repr__(self):
141 def __repr__(self):
142 if hasattr(self, '__unicode__'):
142 if hasattr(self, '__unicode__'):
143 # python repr needs to return str
143 # python repr needs to return str
144 return safe_str(self.__unicode__())
144 return safe_str(self.__unicode__())
145 return '<DB:%s>' % (self.__class__.__name__)
145 return '<DB:%s>' % (self.__class__.__name__)
146
146
147
147
148 class RhodeCodeSetting(Base, BaseModel):
148 class RhodeCodeSetting(Base, BaseModel):
149 __tablename__ = 'rhodecode_settings'
149 __tablename__ = 'rhodecode_settings'
150 __table_args__ = (
150 __table_args__ = (
151 UniqueConstraint('app_settings_name'),
151 UniqueConstraint('app_settings_name'),
152 {'extend_existing': True, 'mysql_engine': 'InnoDB',
152 {'extend_existing': True, 'mysql_engine': 'InnoDB',
153 'mysql_charset': 'utf8'}
153 'mysql_charset': 'utf8'}
154 )
154 )
155 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
155 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
156 app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
156 app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
157 _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
157 _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
158
158
159 def __init__(self, k='', v=''):
159 def __init__(self, k='', v=''):
160 self.app_settings_name = k
160 self.app_settings_name = k
161 self.app_settings_value = v
161 self.app_settings_value = v
162
162
163 @validates('_app_settings_value')
163 @validates('_app_settings_value')
164 def validate_settings_value(self, key, val):
164 def validate_settings_value(self, key, val):
165 assert type(val) == unicode
165 assert type(val) == unicode
166 return val
166 return val
167
167
168 @hybrid_property
168 @hybrid_property
169 def app_settings_value(self):
169 def app_settings_value(self):
170 v = self._app_settings_value
170 v = self._app_settings_value
171 if self.app_settings_name in ["ldap_active",
171 if self.app_settings_name in ["ldap_active",
172 "default_repo_enable_statistics",
172 "default_repo_enable_statistics",
173 "default_repo_enable_locking",
173 "default_repo_enable_locking",
174 "default_repo_private",
174 "default_repo_private",
175 "default_repo_enable_downloads"]:
175 "default_repo_enable_downloads"]:
176 v = str2bool(v)
176 v = str2bool(v)
177 return v
177 return v
178
178
179 @app_settings_value.setter
179 @app_settings_value.setter
180 def app_settings_value(self, val):
180 def app_settings_value(self, val):
181 """
181 """
182 Setter that will always make sure we use unicode in app_settings_value
182 Setter that will always make sure we use unicode in app_settings_value
183
183
184 :param val:
184 :param val:
185 """
185 """
186 self._app_settings_value = safe_unicode(val)
186 self._app_settings_value = safe_unicode(val)
187
187
188 def __unicode__(self):
188 def __unicode__(self):
189 return u"<%s('%s:%s')>" % (
189 return u"<%s('%s:%s')>" % (
190 self.__class__.__name__,
190 self.__class__.__name__,
191 self.app_settings_name, self.app_settings_value
191 self.app_settings_name, self.app_settings_value
192 )
192 )
193
193
194 @classmethod
194 @classmethod
195 def get_by_name(cls, key):
195 def get_by_name(cls, key):
196 return cls.query()\
196 return cls.query()\
197 .filter(cls.app_settings_name == key).scalar()
197 .filter(cls.app_settings_name == key).scalar()
198
198
199 @classmethod
199 @classmethod
200 def get_by_name_or_create(cls, key):
200 def get_by_name_or_create(cls, key):
201 res = cls.get_by_name(key)
201 res = cls.get_by_name(key)
202 if not res:
202 if not res:
203 res = cls(key)
203 res = cls(key)
204 return res
204 return res
205
205
206 @classmethod
206 @classmethod
207 def get_app_settings(cls, cache=False):
207 def get_app_settings(cls, cache=False):
208
208
209 ret = cls.query()
209 ret = cls.query()
210
210
211 if cache:
211 if cache:
212 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
212 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
213
213
214 if not ret:
214 if not ret:
215 raise Exception('Could not get application settings !')
215 raise Exception('Could not get application settings !')
216 settings = {}
216 settings = {}
217 for each in ret:
217 for each in ret:
218 settings['rhodecode_' + each.app_settings_name] = \
218 settings['rhodecode_' + each.app_settings_name] = \
219 each.app_settings_value
219 each.app_settings_value
220
220
221 return settings
221 return settings
222
222
223 @classmethod
223 @classmethod
224 def get_ldap_settings(cls, cache=False):
224 def get_ldap_settings(cls, cache=False):
225 ret = cls.query()\
225 ret = cls.query()\
226 .filter(cls.app_settings_name.startswith('ldap_')).all()
226 .filter(cls.app_settings_name.startswith('ldap_')).all()
227 fd = {}
227 fd = {}
228 for row in ret:
228 for row in ret:
229 fd.update({row.app_settings_name: row.app_settings_value})
229 fd.update({row.app_settings_name: row.app_settings_value})
230
230
231 return fd
231 return fd
232
232
233 @classmethod
233 @classmethod
234 def get_default_repo_settings(cls, cache=False, strip_prefix=False):
234 def get_default_repo_settings(cls, cache=False, strip_prefix=False):
235 ret = cls.query()\
235 ret = cls.query()\
236 .filter(cls.app_settings_name.startswith('default_')).all()
236 .filter(cls.app_settings_name.startswith('default_')).all()
237 fd = {}
237 fd = {}
238 for row in ret:
238 for row in ret:
239 key = row.app_settings_name
239 key = row.app_settings_name
240 if strip_prefix:
240 if strip_prefix:
241 key = remove_prefix(key, prefix='default_')
241 key = remove_prefix(key, prefix='default_')
242 fd.update({key: row.app_settings_value})
242 fd.update({key: row.app_settings_value})
243
243
244 return fd
244 return fd
245
245
246
246
247 class RhodeCodeUi(Base, BaseModel):
247 class RhodeCodeUi(Base, BaseModel):
248 __tablename__ = 'rhodecode_ui'
248 __tablename__ = 'rhodecode_ui'
249 __table_args__ = (
249 __table_args__ = (
250 UniqueConstraint('ui_key'),
250 UniqueConstraint('ui_key'),
251 {'extend_existing': True, 'mysql_engine': 'InnoDB',
251 {'extend_existing': True, 'mysql_engine': 'InnoDB',
252 'mysql_charset': 'utf8'}
252 'mysql_charset': 'utf8'}
253 )
253 )
254
254
255 HOOK_UPDATE = 'changegroup.update'
255 HOOK_UPDATE = 'changegroup.update'
256 HOOK_REPO_SIZE = 'changegroup.repo_size'
256 HOOK_REPO_SIZE = 'changegroup.repo_size'
257 HOOK_PUSH = 'changegroup.push_logger'
257 HOOK_PUSH = 'changegroup.push_logger'
258 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
258 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
259 HOOK_PULL = 'outgoing.pull_logger'
259 HOOK_PULL = 'outgoing.pull_logger'
260 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
260 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
261
261
262 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
262 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
263 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
263 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
264 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
264 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
265 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
265 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
266 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
266 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
267
267
268 @classmethod
268 @classmethod
269 def get_by_key(cls, key):
269 def get_by_key(cls, key):
270 return cls.query().filter(cls.ui_key == key).scalar()
270 return cls.query().filter(cls.ui_key == key).scalar()
271
271
272 @classmethod
272 @classmethod
273 def get_builtin_hooks(cls):
273 def get_builtin_hooks(cls):
274 q = cls.query()
274 q = cls.query()
275 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
275 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
276 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
276 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
277 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
277 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
278 return q.all()
278 return q.all()
279
279
280 @classmethod
280 @classmethod
281 def get_custom_hooks(cls):
281 def get_custom_hooks(cls):
282 q = cls.query()
282 q = cls.query()
283 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
283 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
284 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
284 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
285 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
285 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
286 q = q.filter(cls.ui_section == 'hooks')
286 q = q.filter(cls.ui_section == 'hooks')
287 return q.all()
287 return q.all()
288
288
289 @classmethod
289 @classmethod
290 def get_repos_location(cls):
290 def get_repos_location(cls):
291 return cls.get_by_key('/').ui_value
291 return cls.get_by_key('/').ui_value
292
292
293 @classmethod
293 @classmethod
294 def create_or_update_hook(cls, key, val):
294 def create_or_update_hook(cls, key, val):
295 new_ui = cls.get_by_key(key) or cls()
295 new_ui = cls.get_by_key(key) or cls()
296 new_ui.ui_section = 'hooks'
296 new_ui.ui_section = 'hooks'
297 new_ui.ui_active = True
297 new_ui.ui_active = True
298 new_ui.ui_key = key
298 new_ui.ui_key = key
299 new_ui.ui_value = val
299 new_ui.ui_value = val
300
300
301 Session().add(new_ui)
301 Session().add(new_ui)
302
302
303 def __repr__(self):
303 def __repr__(self):
304 return '<DB:%s[%s:%s]>' % (self.__class__.__name__, self.ui_key,
304 return '<DB:%s[%s:%s]>' % (self.__class__.__name__, self.ui_key,
305 self.ui_value)
305 self.ui_value)
306
306
307
307
308 class User(Base, BaseModel):
308 class User(Base, BaseModel):
309 __tablename__ = 'users'
309 __tablename__ = 'users'
310 __table_args__ = (
310 __table_args__ = (
311 UniqueConstraint('username'), UniqueConstraint('email'),
311 UniqueConstraint('username'), UniqueConstraint('email'),
312 Index('u_username_idx', 'username'),
312 Index('u_username_idx', 'username'),
313 Index('u_email_idx', 'email'),
313 Index('u_email_idx', 'email'),
314 {'extend_existing': True, 'mysql_engine': 'InnoDB',
314 {'extend_existing': True, 'mysql_engine': 'InnoDB',
315 'mysql_charset': 'utf8'}
315 'mysql_charset': 'utf8'}
316 )
316 )
317 DEFAULT_USER = 'default'
317 DEFAULT_USER = 'default'
318 DEFAULT_PERMISSIONS = [
318 DEFAULT_PERMISSIONS = [
319 'hg.register.manual_activate', 'hg.create.repository',
319 'hg.register.manual_activate', 'hg.create.repository',
320 'hg.fork.repository', 'repository.read', 'group.read'
320 'hg.fork.repository', 'repository.read', 'group.read'
321 ]
321 ]
322 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
322 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
323 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
323 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
324 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
324 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
325 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
325 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
326 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
326 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
327 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
327 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
328 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
328 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
329 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
329 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
330 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
330 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
331 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
331 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
332 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
332 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
333 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
333 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
334
334
335 user_log = relationship('UserLog')
335 user_log = relationship('UserLog')
336 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
336 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
337
337
338 repositories = relationship('Repository')
338 repositories = relationship('Repository')
339 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
339 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
340 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
340 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
341
341
342 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
342 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
343 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
343 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
344
344
345 group_member = relationship('UserGroupMember', cascade='all')
345 group_member = relationship('UserGroupMember', cascade='all')
346
346
347 notifications = relationship('UserNotification', cascade='all')
347 notifications = relationship('UserNotification', cascade='all')
348 # notifications assigned to this user
348 # notifications assigned to this user
349 user_created_notifications = relationship('Notification', cascade='all')
349 user_created_notifications = relationship('Notification', cascade='all')
350 # comments created by this user
350 # comments created by this user
351 user_comments = relationship('ChangesetComment', cascade='all')
351 user_comments = relationship('ChangesetComment', cascade='all')
352 #extra emails for this user
352 #extra emails for this user
353 user_emails = relationship('UserEmailMap', cascade='all')
353 user_emails = relationship('UserEmailMap', cascade='all')
354
354
355 @hybrid_property
355 @hybrid_property
356 def email(self):
356 def email(self):
357 return self._email
357 return self._email
358
358
359 @email.setter
359 @email.setter
360 def email(self, val):
360 def email(self, val):
361 self._email = val.lower() if val else None
361 self._email = val.lower() if val else None
362
362
363 @property
363 @property
364 def firstname(self):
364 def firstname(self):
365 # alias for future
365 # alias for future
366 return self.name
366 return self.name
367
367
368 @property
368 @property
369 def emails(self):
369 def emails(self):
370 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
370 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
371 return [self.email] + [x.email for x in other]
371 return [self.email] + [x.email for x in other]
372
372
373 @property
373 @property
374 def ip_addresses(self):
374 def ip_addresses(self):
375 ret = UserIpMap.query().filter(UserIpMap.user == self).all()
375 ret = UserIpMap.query().filter(UserIpMap.user == self).all()
376 return [x.ip_addr for x in ret]
376 return [x.ip_addr for x in ret]
377
377
378 @property
378 @property
379 def username_and_name(self):
379 def username_and_name(self):
380 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
380 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
381
381
382 @property
382 @property
383 def full_name(self):
383 def full_name(self):
384 return '%s %s' % (self.firstname, self.lastname)
384 return '%s %s' % (self.firstname, self.lastname)
385
385
386 @property
386 @property
387 def full_name_or_username(self):
387 def full_name_or_username(self):
388 return ('%s %s' % (self.firstname, self.lastname)
388 return ('%s %s' % (self.firstname, self.lastname)
389 if (self.firstname and self.lastname) else self.username)
389 if (self.firstname and self.lastname) else self.username)
390
390
391 @property
391 @property
392 def full_contact(self):
392 def full_contact(self):
393 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
393 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
394
394
395 @property
395 @property
396 def short_contact(self):
396 def short_contact(self):
397 return '%s %s' % (self.firstname, self.lastname)
397 return '%s %s' % (self.firstname, self.lastname)
398
398
399 @property
399 @property
400 def is_admin(self):
400 def is_admin(self):
401 return self.admin
401 return self.admin
402
402
403 @property
403 @property
404 def AuthUser(self):
404 def AuthUser(self):
405 """
405 """
406 Returns instance of AuthUser for this user
406 Returns instance of AuthUser for this user
407 """
407 """
408 from rhodecode.lib.auth import AuthUser
408 from rhodecode.lib.auth import AuthUser
409 return AuthUser(user_id=self.user_id, api_key=self.api_key,
409 return AuthUser(user_id=self.user_id, api_key=self.api_key,
410 username=self.username)
410 username=self.username)
411
411
412 def __unicode__(self):
412 def __unicode__(self):
413 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
413 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
414 self.user_id, self.username)
414 self.user_id, self.username)
415
415
416 @classmethod
416 @classmethod
417 def get_by_username(cls, username, case_insensitive=False, cache=False):
417 def get_by_username(cls, username, case_insensitive=False, cache=False):
418 if case_insensitive:
418 if case_insensitive:
419 q = cls.query().filter(cls.username.ilike(username))
419 q = cls.query().filter(cls.username.ilike(username))
420 else:
420 else:
421 q = cls.query().filter(cls.username == username)
421 q = cls.query().filter(cls.username == username)
422
422
423 if cache:
423 if cache:
424 q = q.options(FromCache(
424 q = q.options(FromCache(
425 "sql_cache_short",
425 "sql_cache_short",
426 "get_user_%s" % _hash_key(username)
426 "get_user_%s" % _hash_key(username)
427 )
427 )
428 )
428 )
429 return q.scalar()
429 return q.scalar()
430
430
431 @classmethod
431 @classmethod
432 def get_by_api_key(cls, api_key, cache=False):
432 def get_by_api_key(cls, api_key, cache=False):
433 q = cls.query().filter(cls.api_key == api_key)
433 q = cls.query().filter(cls.api_key == api_key)
434
434
435 if cache:
435 if cache:
436 q = q.options(FromCache("sql_cache_short",
436 q = q.options(FromCache("sql_cache_short",
437 "get_api_key_%s" % api_key))
437 "get_api_key_%s" % api_key))
438 return q.scalar()
438 return q.scalar()
439
439
440 @classmethod
440 @classmethod
441 def get_by_email(cls, email, case_insensitive=False, cache=False):
441 def get_by_email(cls, email, case_insensitive=False, cache=False):
442 if case_insensitive:
442 if case_insensitive:
443 q = cls.query().filter(cls.email.ilike(email))
443 q = cls.query().filter(cls.email.ilike(email))
444 else:
444 else:
445 q = cls.query().filter(cls.email == email)
445 q = cls.query().filter(cls.email == email)
446
446
447 if cache:
447 if cache:
448 q = q.options(FromCache("sql_cache_short",
448 q = q.options(FromCache("sql_cache_short",
449 "get_email_key_%s" % email))
449 "get_email_key_%s" % email))
450
450
451 ret = q.scalar()
451 ret = q.scalar()
452 if ret is None:
452 if ret is None:
453 q = UserEmailMap.query()
453 q = UserEmailMap.query()
454 # try fetching in alternate email map
454 # try fetching in alternate email map
455 if case_insensitive:
455 if case_insensitive:
456 q = q.filter(UserEmailMap.email.ilike(email))
456 q = q.filter(UserEmailMap.email.ilike(email))
457 else:
457 else:
458 q = q.filter(UserEmailMap.email == email)
458 q = q.filter(UserEmailMap.email == email)
459 q = q.options(joinedload(UserEmailMap.user))
459 q = q.options(joinedload(UserEmailMap.user))
460 if cache:
460 if cache:
461 q = q.options(FromCache("sql_cache_short",
461 q = q.options(FromCache("sql_cache_short",
462 "get_email_map_key_%s" % email))
462 "get_email_map_key_%s" % email))
463 ret = getattr(q.scalar(), 'user', None)
463 ret = getattr(q.scalar(), 'user', None)
464
464
465 return ret
465 return ret
466
466
467 @classmethod
467 @classmethod
468 def get_from_cs_author(cls, author):
468 def get_from_cs_author(cls, author):
469 """
469 """
470 Tries to get User objects out of commit author string
470 Tries to get User objects out of commit author string
471
471
472 :param author:
472 :param author:
473 """
473 """
474 from rhodecode.lib.helpers import email, author_name
474 from rhodecode.lib.helpers import email, author_name
475 # Valid email in the attribute passed, see if they're in the system
475 # Valid email in the attribute passed, see if they're in the system
476 _email = email(author)
476 _email = email(author)
477 if _email:
477 if _email:
478 user = cls.get_by_email(_email, case_insensitive=True)
478 user = cls.get_by_email(_email, case_insensitive=True)
479 if user:
479 if user:
480 return user
480 return user
481 # Maybe we can match by username?
481 # Maybe we can match by username?
482 _author = author_name(author)
482 _author = author_name(author)
483 user = cls.get_by_username(_author, case_insensitive=True)
483 user = cls.get_by_username(_author, case_insensitive=True)
484 if user:
484 if user:
485 return user
485 return user
486
486
487 def update_lastlogin(self):
487 def update_lastlogin(self):
488 """Update user lastlogin"""
488 """Update user lastlogin"""
489 self.last_login = datetime.datetime.now()
489 self.last_login = datetime.datetime.now()
490 Session().add(self)
490 Session().add(self)
491 log.debug('updated user %s lastlogin' % self.username)
491 log.debug('updated user %s lastlogin' % self.username)
492
492
493 def get_api_data(self):
493 def get_api_data(self):
494 """
494 """
495 Common function for generating user related data for API
495 Common function for generating user related data for API
496 """
496 """
497 user = self
497 user = self
498 data = dict(
498 data = dict(
499 user_id=user.user_id,
499 user_id=user.user_id,
500 username=user.username,
500 username=user.username,
501 firstname=user.name,
501 firstname=user.name,
502 lastname=user.lastname,
502 lastname=user.lastname,
503 email=user.email,
503 email=user.email,
504 emails=user.emails,
504 emails=user.emails,
505 api_key=user.api_key,
505 api_key=user.api_key,
506 active=user.active,
506 active=user.active,
507 admin=user.admin,
507 admin=user.admin,
508 ldap_dn=user.ldap_dn,
508 ldap_dn=user.ldap_dn,
509 last_login=user.last_login,
509 last_login=user.last_login,
510 ip_addresses=user.ip_addresses
510 ip_addresses=user.ip_addresses
511 )
511 )
512 return data
512 return data
513
513
514 def __json__(self):
514 def __json__(self):
515 data = dict(
515 data = dict(
516 full_name=self.full_name,
516 full_name=self.full_name,
517 full_name_or_username=self.full_name_or_username,
517 full_name_or_username=self.full_name_or_username,
518 short_contact=self.short_contact,
518 short_contact=self.short_contact,
519 full_contact=self.full_contact
519 full_contact=self.full_contact
520 )
520 )
521 data.update(self.get_api_data())
521 data.update(self.get_api_data())
522 return data
522 return data
523
523
524
524
525 class UserEmailMap(Base, BaseModel):
525 class UserEmailMap(Base, BaseModel):
526 __tablename__ = 'user_email_map'
526 __tablename__ = 'user_email_map'
527 __table_args__ = (
527 __table_args__ = (
528 Index('uem_email_idx', 'email'),
528 Index('uem_email_idx', 'email'),
529 UniqueConstraint('email'),
529 UniqueConstraint('email'),
530 {'extend_existing': True, 'mysql_engine': 'InnoDB',
530 {'extend_existing': True, 'mysql_engine': 'InnoDB',
531 'mysql_charset': 'utf8'}
531 'mysql_charset': 'utf8'}
532 )
532 )
533 __mapper_args__ = {}
533 __mapper_args__ = {}
534
534
535 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
535 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
536 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
536 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
537 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
537 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
538 user = relationship('User', lazy='joined')
538 user = relationship('User', lazy='joined')
539
539
540 @validates('_email')
540 @validates('_email')
541 def validate_email(self, key, email):
541 def validate_email(self, key, email):
542 # check if this email is not main one
542 # check if this email is not main one
543 main_email = Session().query(User).filter(User.email == email).scalar()
543 main_email = Session().query(User).filter(User.email == email).scalar()
544 if main_email is not None:
544 if main_email is not None:
545 raise AttributeError('email %s is present is user table' % email)
545 raise AttributeError('email %s is present is user table' % email)
546 return email
546 return email
547
547
548 @hybrid_property
548 @hybrid_property
549 def email(self):
549 def email(self):
550 return self._email
550 return self._email
551
551
552 @email.setter
552 @email.setter
553 def email(self, val):
553 def email(self, val):
554 self._email = val.lower() if val else None
554 self._email = val.lower() if val else None
555
555
556
556
557 class UserIpMap(Base, BaseModel):
557 class UserIpMap(Base, BaseModel):
558 __tablename__ = 'user_ip_map'
558 __tablename__ = 'user_ip_map'
559 __table_args__ = (
559 __table_args__ = (
560 UniqueConstraint('user_id', 'ip_addr'),
560 UniqueConstraint('user_id', 'ip_addr'),
561 {'extend_existing': True, 'mysql_engine': 'InnoDB',
561 {'extend_existing': True, 'mysql_engine': 'InnoDB',
562 'mysql_charset': 'utf8'}
562 'mysql_charset': 'utf8'}
563 )
563 )
564 __mapper_args__ = {}
564 __mapper_args__ = {}
565
565
566 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
566 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
567 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
567 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
568 ip_addr = Column("ip_addr", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
568 ip_addr = Column("ip_addr", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
569 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
569 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
570 user = relationship('User', lazy='joined')
570 user = relationship('User', lazy='joined')
571
571
572 @classmethod
572 @classmethod
573 def _get_ip_range(cls, ip_addr):
573 def _get_ip_range(cls, ip_addr):
574 from rhodecode.lib import ipaddr
574 from rhodecode.lib import ipaddr
575 net = ipaddr.IPNetwork(address=ip_addr)
575 net = ipaddr.IPNetwork(address=ip_addr)
576 return [str(net.network), str(net.broadcast)]
576 return [str(net.network), str(net.broadcast)]
577
577
578 def __json__(self):
578 def __json__(self):
579 return dict(
579 return dict(
580 ip_addr=self.ip_addr,
580 ip_addr=self.ip_addr,
581 ip_range=self._get_ip_range(self.ip_addr)
581 ip_range=self._get_ip_range(self.ip_addr)
582 )
582 )
583
583
584
584
585 class UserLog(Base, BaseModel):
585 class UserLog(Base, BaseModel):
586 __tablename__ = 'user_logs'
586 __tablename__ = 'user_logs'
587 __table_args__ = (
587 __table_args__ = (
588 {'extend_existing': True, 'mysql_engine': 'InnoDB',
588 {'extend_existing': True, 'mysql_engine': 'InnoDB',
589 'mysql_charset': 'utf8'},
589 'mysql_charset': 'utf8'},
590 )
590 )
591 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
591 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
592 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
592 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
593 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
593 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
594 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
594 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
595 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
595 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
596 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
596 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
597 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
597 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
598 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
598 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
599
599
600 @property
600 @property
601 def action_as_day(self):
601 def action_as_day(self):
602 return datetime.date(*self.action_date.timetuple()[:3])
602 return datetime.date(*self.action_date.timetuple()[:3])
603
603
604 user = relationship('User')
604 user = relationship('User')
605 repository = relationship('Repository', cascade='')
605 repository = relationship('Repository', cascade='')
606
606
607
607
608 class UserGroup(Base, BaseModel):
608 class UserGroup(Base, BaseModel):
609 __tablename__ = 'users_groups'
609 __tablename__ = 'users_groups'
610 __table_args__ = (
610 __table_args__ = (
611 {'extend_existing': True, 'mysql_engine': 'InnoDB',
611 {'extend_existing': True, 'mysql_engine': 'InnoDB',
612 'mysql_charset': 'utf8'},
612 'mysql_charset': 'utf8'},
613 )
613 )
614
614
615 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
615 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
616 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
616 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
617 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
617 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
618 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
618 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
619
619
620 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
620 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
621 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
621 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
622 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
622 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
623
623
624 def __unicode__(self):
624 def __unicode__(self):
625 return u'<userGroup(%s)>' % (self.users_group_name)
625 return u'<userGroup(%s)>' % (self.users_group_name)
626
626
627 @classmethod
627 @classmethod
628 def get_by_group_name(cls, group_name, cache=False,
628 def get_by_group_name(cls, group_name, cache=False,
629 case_insensitive=False):
629 case_insensitive=False):
630 if case_insensitive:
630 if case_insensitive:
631 q = cls.query().filter(cls.users_group_name.ilike(group_name))
631 q = cls.query().filter(cls.users_group_name.ilike(group_name))
632 else:
632 else:
633 q = cls.query().filter(cls.users_group_name == group_name)
633 q = cls.query().filter(cls.users_group_name == group_name)
634 if cache:
634 if cache:
635 q = q.options(FromCache(
635 q = q.options(FromCache(
636 "sql_cache_short",
636 "sql_cache_short",
637 "get_user_%s" % _hash_key(group_name)
637 "get_user_%s" % _hash_key(group_name)
638 )
638 )
639 )
639 )
640 return q.scalar()
640 return q.scalar()
641
641
642 @classmethod
642 @classmethod
643 def get(cls, users_group_id, cache=False):
643 def get(cls, users_group_id, cache=False):
644 users_group = cls.query()
644 users_group = cls.query()
645 if cache:
645 if cache:
646 users_group = users_group.options(FromCache("sql_cache_short",
646 users_group = users_group.options(FromCache("sql_cache_short",
647 "get_users_group_%s" % users_group_id))
647 "get_users_group_%s" % users_group_id))
648 return users_group.get(users_group_id)
648 return users_group.get(users_group_id)
649
649
650 def get_api_data(self):
650 def get_api_data(self):
651 users_group = self
651 users_group = self
652
652
653 data = dict(
653 data = dict(
654 users_group_id=users_group.users_group_id,
654 users_group_id=users_group.users_group_id,
655 group_name=users_group.users_group_name,
655 group_name=users_group.users_group_name,
656 active=users_group.users_group_active,
656 active=users_group.users_group_active,
657 )
657 )
658
658
659 return data
659 return data
660
660
661
661
662 class UserGroupMember(Base, BaseModel):
662 class UserGroupMember(Base, BaseModel):
663 __tablename__ = 'users_groups_members'
663 __tablename__ = 'users_groups_members'
664 __table_args__ = (
664 __table_args__ = (
665 {'extend_existing': True, 'mysql_engine': 'InnoDB',
665 {'extend_existing': True, 'mysql_engine': 'InnoDB',
666 'mysql_charset': 'utf8'},
666 'mysql_charset': 'utf8'},
667 )
667 )
668
668
669 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
669 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
670 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
670 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
671 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
671 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
672
672
673 user = relationship('User', lazy='joined')
673 user = relationship('User', lazy='joined')
674 users_group = relationship('UserGroup')
674 users_group = relationship('UserGroup')
675
675
676 def __init__(self, gr_id='', u_id=''):
676 def __init__(self, gr_id='', u_id=''):
677 self.users_group_id = gr_id
677 self.users_group_id = gr_id
678 self.user_id = u_id
678 self.user_id = u_id
679
679
680
680
681 class RepositoryField(Base, BaseModel):
681 class RepositoryField(Base, BaseModel):
682 __tablename__ = 'repositories_fields'
682 __tablename__ = 'repositories_fields'
683 __table_args__ = (
683 __table_args__ = (
684 UniqueConstraint('repository_id', 'field_key'), # no-multi field
684 UniqueConstraint('repository_id', 'field_key'), # no-multi field
685 {'extend_existing': True, 'mysql_engine': 'InnoDB',
685 {'extend_existing': True, 'mysql_engine': 'InnoDB',
686 'mysql_charset': 'utf8'},
686 'mysql_charset': 'utf8'},
687 )
687 )
688 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
688 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
689
689
690 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
690 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
691 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
691 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
692 field_key = Column("field_key", String(250, convert_unicode=False, assert_unicode=None))
692 field_key = Column("field_key", String(250, convert_unicode=False, assert_unicode=None))
693 field_label = Column("field_label", String(1024, convert_unicode=False, assert_unicode=None), nullable=False)
693 field_label = Column("field_label", String(1024, convert_unicode=False, assert_unicode=None), nullable=False)
694 field_value = Column("field_value", String(10000, convert_unicode=False, assert_unicode=None), nullable=False)
694 field_value = Column("field_value", String(10000, convert_unicode=False, assert_unicode=None), nullable=False)
695 field_desc = Column("field_desc", String(1024, convert_unicode=False, assert_unicode=None), nullable=False)
695 field_desc = Column("field_desc", String(1024, convert_unicode=False, assert_unicode=None), nullable=False)
696 field_type = Column("field_type", String(256), nullable=False, unique=None)
696 field_type = Column("field_type", String(256), nullable=False, unique=None)
697 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
697 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
698
698
699 repository = relationship('Repository')
699 repository = relationship('Repository')
700
700
701 @property
701 @property
702 def field_key_prefixed(self):
702 def field_key_prefixed(self):
703 return 'ex_%s' % self.field_key
703 return 'ex_%s' % self.field_key
704
704
705 @classmethod
705 @classmethod
706 def un_prefix_key(cls, key):
706 def un_prefix_key(cls, key):
707 if key.startswith(cls.PREFIX):
707 if key.startswith(cls.PREFIX):
708 return key[len(cls.PREFIX):]
708 return key[len(cls.PREFIX):]
709 return key
709 return key
710
710
711 @classmethod
711 @classmethod
712 def get_by_key_name(cls, key, repo):
712 def get_by_key_name(cls, key, repo):
713 row = cls.query()\
713 row = cls.query()\
714 .filter(cls.repository == repo)\
714 .filter(cls.repository == repo)\
715 .filter(cls.field_key == key).scalar()
715 .filter(cls.field_key == key).scalar()
716 return row
716 return row
717
717
718
718
719 class Repository(Base, BaseModel):
719 class Repository(Base, BaseModel):
720 __tablename__ = 'repositories'
720 __tablename__ = 'repositories'
721 __table_args__ = (
721 __table_args__ = (
722 UniqueConstraint('repo_name'),
722 UniqueConstraint('repo_name'),
723 Index('r_repo_name_idx', 'repo_name'),
723 Index('r_repo_name_idx', 'repo_name'),
724 {'extend_existing': True, 'mysql_engine': 'InnoDB',
724 {'extend_existing': True, 'mysql_engine': 'InnoDB',
725 'mysql_charset': 'utf8'},
725 'mysql_charset': 'utf8'},
726 )
726 )
727
727
728 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
728 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
729 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
729 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
730 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
730 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
731 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
731 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
732 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
732 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
733 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
733 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
734 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
734 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
735 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
735 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
736 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
736 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
737 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
737 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
738 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
738 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
739 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
739 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
740 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
740 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
741 _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
741 _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
742 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
742 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
743
743
744 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
744 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
745 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
745 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
746
746
747 user = relationship('User')
747 user = relationship('User')
748 fork = relationship('Repository', remote_side=repo_id)
748 fork = relationship('Repository', remote_side=repo_id)
749 group = relationship('RepoGroup')
749 group = relationship('RepoGroup')
750 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
750 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
751 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
751 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
752 stats = relationship('Statistics', cascade='all', uselist=False)
752 stats = relationship('Statistics', cascade='all', uselist=False)
753
753
754 followers = relationship('UserFollowing',
754 followers = relationship('UserFollowing',
755 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
755 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
756 cascade='all')
756 cascade='all')
757 extra_fields = relationship('RepositoryField',
757 extra_fields = relationship('RepositoryField',
758 cascade="all, delete, delete-orphan")
758 cascade="all, delete, delete-orphan")
759
759
760 logs = relationship('UserLog')
760 logs = relationship('UserLog')
761 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
761 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
762
762
763 pull_requests_org = relationship('PullRequest',
763 pull_requests_org = relationship('PullRequest',
764 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
764 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
765 cascade="all, delete, delete-orphan")
765 cascade="all, delete, delete-orphan")
766
766
767 pull_requests_other = relationship('PullRequest',
767 pull_requests_other = relationship('PullRequest',
768 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
768 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
769 cascade="all, delete, delete-orphan")
769 cascade="all, delete, delete-orphan")
770
770
771 def __unicode__(self):
771 def __unicode__(self):
772 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
772 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
773 self.repo_name)
773 self.repo_name)
774
774
775 @hybrid_property
775 @hybrid_property
776 def locked(self):
776 def locked(self):
777 # always should return [user_id, timelocked]
777 # always should return [user_id, timelocked]
778 if self._locked:
778 if self._locked:
779 _lock_info = self._locked.split(':')
779 _lock_info = self._locked.split(':')
780 return int(_lock_info[0]), _lock_info[1]
780 return int(_lock_info[0]), _lock_info[1]
781 return [None, None]
781 return [None, None]
782
782
783 @locked.setter
783 @locked.setter
784 def locked(self, val):
784 def locked(self, val):
785 if val and isinstance(val, (list, tuple)):
785 if val and isinstance(val, (list, tuple)):
786 self._locked = ':'.join(map(str, val))
786 self._locked = ':'.join(map(str, val))
787 else:
787 else:
788 self._locked = None
788 self._locked = None
789
789
790 @hybrid_property
790 @hybrid_property
791 def changeset_cache(self):
791 def changeset_cache(self):
792 from rhodecode.lib.vcs.backends.base import EmptyChangeset
792 from rhodecode.lib.vcs.backends.base import EmptyChangeset
793 dummy = EmptyChangeset().__json__()
793 dummy = EmptyChangeset().__json__()
794 if not self._changeset_cache:
794 if not self._changeset_cache:
795 return dummy
795 return dummy
796 try:
796 try:
797 return json.loads(self._changeset_cache)
797 return json.loads(self._changeset_cache)
798 except TypeError:
798 except TypeError:
799 return dummy
799 return dummy
800
800
801 @changeset_cache.setter
801 @changeset_cache.setter
802 def changeset_cache(self, val):
802 def changeset_cache(self, val):
803 try:
803 try:
804 self._changeset_cache = json.dumps(val)
804 self._changeset_cache = json.dumps(val)
805 except Exception:
805 except Exception:
806 log.error(traceback.format_exc())
806 log.error(traceback.format_exc())
807
807
808 @classmethod
808 @classmethod
809 def url_sep(cls):
809 def url_sep(cls):
810 return URL_SEP
810 return URL_SEP
811
811
812 @classmethod
812 @classmethod
813 def normalize_repo_name(cls, repo_name):
813 def normalize_repo_name(cls, repo_name):
814 """
814 """
815 Normalizes os specific repo_name to the format internally stored inside
815 Normalizes os specific repo_name to the format internally stored inside
816 dabatabase using URL_SEP
816 dabatabase using URL_SEP
817
817
818 :param cls:
818 :param cls:
819 :param repo_name:
819 :param repo_name:
820 """
820 """
821 return cls.url_sep().join(repo_name.split(os.sep))
821 return cls.url_sep().join(repo_name.split(os.sep))
822
822
823 @classmethod
823 @classmethod
824 def get_by_repo_name(cls, repo_name):
824 def get_by_repo_name(cls, repo_name):
825 q = Session().query(cls).filter(cls.repo_name == repo_name)
825 q = Session().query(cls).filter(cls.repo_name == repo_name)
826 q = q.options(joinedload(Repository.fork))\
826 q = q.options(joinedload(Repository.fork))\
827 .options(joinedload(Repository.user))\
827 .options(joinedload(Repository.user))\
828 .options(joinedload(Repository.group))
828 .options(joinedload(Repository.group))
829 return q.scalar()
829 return q.scalar()
830
830
831 @classmethod
831 @classmethod
832 def get_by_full_path(cls, repo_full_path):
832 def get_by_full_path(cls, repo_full_path):
833 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
833 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
834 repo_name = cls.normalize_repo_name(repo_name)
834 repo_name = cls.normalize_repo_name(repo_name)
835 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
835 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
836
836
837 @classmethod
837 @classmethod
838 def get_repo_forks(cls, repo_id):
838 def get_repo_forks(cls, repo_id):
839 return cls.query().filter(Repository.fork_id == repo_id)
839 return cls.query().filter(Repository.fork_id == repo_id)
840
840
841 @classmethod
841 @classmethod
842 def base_path(cls):
842 def base_path(cls):
843 """
843 """
844 Returns base path when all repos are stored
844 Returns base path when all repos are stored
845
845
846 :param cls:
846 :param cls:
847 """
847 """
848 q = Session().query(RhodeCodeUi)\
848 q = Session().query(RhodeCodeUi)\
849 .filter(RhodeCodeUi.ui_key == cls.url_sep())
849 .filter(RhodeCodeUi.ui_key == cls.url_sep())
850 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
850 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
851 return q.one().ui_value
851 return q.one().ui_value
852
852
853 @property
853 @property
854 def forks(self):
854 def forks(self):
855 """
855 """
856 Return forks of this repo
856 Return forks of this repo
857 """
857 """
858 return Repository.get_repo_forks(self.repo_id)
858 return Repository.get_repo_forks(self.repo_id)
859
859
860 @property
860 @property
861 def parent(self):
861 def parent(self):
862 """
862 """
863 Returns fork parent
863 Returns fork parent
864 """
864 """
865 return self.fork
865 return self.fork
866
866
867 @property
867 @property
868 def just_name(self):
868 def just_name(self):
869 return self.repo_name.split(Repository.url_sep())[-1]
869 return self.repo_name.split(Repository.url_sep())[-1]
870
870
871 @property
871 @property
872 def groups_with_parents(self):
872 def groups_with_parents(self):
873 groups = []
873 groups = []
874 if self.group is None:
874 if self.group is None:
875 return groups
875 return groups
876
876
877 cur_gr = self.group
877 cur_gr = self.group
878 groups.insert(0, cur_gr)
878 groups.insert(0, cur_gr)
879 while 1:
879 while 1:
880 gr = getattr(cur_gr, 'parent_group', None)
880 gr = getattr(cur_gr, 'parent_group', None)
881 cur_gr = cur_gr.parent_group
881 cur_gr = cur_gr.parent_group
882 if gr is None:
882 if gr is None:
883 break
883 break
884 groups.insert(0, gr)
884 groups.insert(0, gr)
885
885
886 return groups
886 return groups
887
887
888 @property
888 @property
889 def groups_and_repo(self):
889 def groups_and_repo(self):
890 return self.groups_with_parents, self.just_name, self.repo_name
890 return self.groups_with_parents, self.just_name, self.repo_name
891
891
892 @LazyProperty
892 @LazyProperty
893 def repo_path(self):
893 def repo_path(self):
894 """
894 """
895 Returns base full path for that repository means where it actually
895 Returns base full path for that repository means where it actually
896 exists on a filesystem
896 exists on a filesystem
897 """
897 """
898 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
898 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
899 Repository.url_sep())
899 Repository.url_sep())
900 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
900 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
901 return q.one().ui_value
901 return q.one().ui_value
902
902
903 @property
903 @property
904 def repo_full_path(self):
904 def repo_full_path(self):
905 p = [self.repo_path]
905 p = [self.repo_path]
906 # we need to split the name by / since this is how we store the
906 # we need to split the name by / since this is how we store the
907 # names in the database, but that eventually needs to be converted
907 # names in the database, but that eventually needs to be converted
908 # into a valid system path
908 # into a valid system path
909 p += self.repo_name.split(Repository.url_sep())
909 p += self.repo_name.split(Repository.url_sep())
910 return os.path.join(*map(safe_unicode, p))
910 return os.path.join(*map(safe_unicode, p))
911
911
912 @property
912 @property
913 def cache_keys(self):
913 def cache_keys(self):
914 """
914 """
915 Returns associated cache keys for that repo
915 Returns associated cache keys for that repo
916 """
916 """
917 return CacheInvalidation.query()\
917 return CacheInvalidation.query()\
918 .filter(CacheInvalidation.cache_args == self.repo_name)\
918 .filter(CacheInvalidation.cache_args == self.repo_name)\
919 .order_by(CacheInvalidation.cache_key)\
919 .order_by(CacheInvalidation.cache_key)\
920 .all()
920 .all()
921
921
922 def get_new_name(self, repo_name):
922 def get_new_name(self, repo_name):
923 """
923 """
924 returns new full repository name based on assigned group and new new
924 returns new full repository name based on assigned group and new new
925
925
926 :param group_name:
926 :param group_name:
927 """
927 """
928 path_prefix = self.group.full_path_splitted if self.group else []
928 path_prefix = self.group.full_path_splitted if self.group else []
929 return Repository.url_sep().join(path_prefix + [repo_name])
929 return Repository.url_sep().join(path_prefix + [repo_name])
930
930
931 @property
931 @property
932 def _ui(self):
932 def _ui(self):
933 """
933 """
934 Creates an db based ui object for this repository
934 Creates an db based ui object for this repository
935 """
935 """
936 from rhodecode.lib.utils import make_ui
936 from rhodecode.lib.utils import make_ui
937 return make_ui('db', clear_session=False)
937 return make_ui('db', clear_session=False)
938
938
939 @classmethod
939 @classmethod
940 def is_valid(cls, repo_name):
940 def is_valid(cls, repo_name):
941 """
941 """
942 returns True if given repo name is a valid filesystem repository
942 returns True if given repo name is a valid filesystem repository
943
943
944 :param cls:
944 :param cls:
945 :param repo_name:
945 :param repo_name:
946 """
946 """
947 from rhodecode.lib.utils import is_valid_repo
947 from rhodecode.lib.utils import is_valid_repo
948
948
949 return is_valid_repo(repo_name, cls.base_path())
949 return is_valid_repo(repo_name, cls.base_path())
950
950
951 def get_api_data(self):
951 def get_api_data(self):
952 """
952 """
953 Common function for generating repo api data
953 Common function for generating repo api data
954
954
955 """
955 """
956 repo = self
956 repo = self
957 data = dict(
957 data = dict(
958 repo_id=repo.repo_id,
958 repo_id=repo.repo_id,
959 repo_name=repo.repo_name,
959 repo_name=repo.repo_name,
960 repo_type=repo.repo_type,
960 repo_type=repo.repo_type,
961 clone_uri=repo.clone_uri,
961 clone_uri=repo.clone_uri,
962 private=repo.private,
962 private=repo.private,
963 created_on=repo.created_on,
963 created_on=repo.created_on,
964 description=repo.description,
964 description=repo.description,
965 landing_rev=repo.landing_rev,
965 landing_rev=repo.landing_rev,
966 owner=repo.user.username,
966 owner=repo.user.username,
967 fork_of=repo.fork.repo_name if repo.fork else None,
967 fork_of=repo.fork.repo_name if repo.fork else None,
968 enable_statistics=repo.enable_statistics,
968 enable_statistics=repo.enable_statistics,
969 enable_locking=repo.enable_locking,
969 enable_locking=repo.enable_locking,
970 enable_downloads=repo.enable_downloads,
970 enable_downloads=repo.enable_downloads,
971 last_changeset=repo.changeset_cache,
971 last_changeset=repo.changeset_cache,
972 locked_by=User.get(self.locked[0]).get_api_data() \
972 locked_by=User.get(self.locked[0]).get_api_data() \
973 if self.locked[0] else None,
973 if self.locked[0] else None,
974 locked_date=time_to_datetime(self.locked[1]) \
974 locked_date=time_to_datetime(self.locked[1]) \
975 if self.locked[1] else None
975 if self.locked[1] else None
976 )
976 )
977 rc_config = RhodeCodeSetting.get_app_settings()
977 rc_config = RhodeCodeSetting.get_app_settings()
978 repository_fields = str2bool(rc_config.get('rhodecode_repository_fields'))
978 repository_fields = str2bool(rc_config.get('rhodecode_repository_fields'))
979 if repository_fields:
979 if repository_fields:
980 for f in self.extra_fields:
980 for f in self.extra_fields:
981 data[f.field_key_prefixed] = f.field_value
981 data[f.field_key_prefixed] = f.field_value
982
982
983 return data
983 return data
984
984
985 @classmethod
985 @classmethod
986 def lock(cls, repo, user_id):
986 def lock(cls, repo, user_id):
987 repo.locked = [user_id, time.time()]
987 repo.locked = [user_id, time.time()]
988 Session().add(repo)
988 Session().add(repo)
989 Session().commit()
989 Session().commit()
990
990
991 @classmethod
991 @classmethod
992 def unlock(cls, repo):
992 def unlock(cls, repo):
993 repo.locked = None
993 repo.locked = None
994 Session().add(repo)
994 Session().add(repo)
995 Session().commit()
995 Session().commit()
996
996
997 @classmethod
997 @classmethod
998 def getlock(cls, repo):
998 def getlock(cls, repo):
999 return repo.locked
999 return repo.locked
1000
1000
1001 @property
1001 @property
1002 def last_db_change(self):
1002 def last_db_change(self):
1003 return self.updated_on
1003 return self.updated_on
1004
1004
1005 def clone_url(self, **override):
1005 def clone_url(self, **override):
1006 from pylons import url
1006 from pylons import url
1007 from urlparse import urlparse
1007 from urlparse import urlparse
1008 import urllib
1008 import urllib
1009 parsed_url = urlparse(url('home', qualified=True))
1009 parsed_url = urlparse(url('home', qualified=True))
1010 default_clone_uri = '%(scheme)s://%(user)s%(pass)s%(netloc)s%(prefix)s%(path)s'
1010 default_clone_uri = '%(scheme)s://%(user)s%(pass)s%(netloc)s%(prefix)s%(path)s'
1011 decoded_path = safe_unicode(urllib.unquote(parsed_url.path))
1011 decoded_path = safe_unicode(urllib.unquote(parsed_url.path))
1012 args = {
1012 args = {
1013 'user': '',
1013 'user': '',
1014 'pass': '',
1014 'pass': '',
1015 'scheme': parsed_url.scheme,
1015 'scheme': parsed_url.scheme,
1016 'netloc': parsed_url.netloc,
1016 'netloc': parsed_url.netloc,
1017 'prefix': decoded_path,
1017 'prefix': decoded_path,
1018 'path': self.repo_name
1018 'path': self.repo_name
1019 }
1019 }
1020
1020
1021 args.update(override)
1021 args.update(override)
1022 return default_clone_uri % args
1022 return default_clone_uri % args
1023
1023
1024 #==========================================================================
1024 #==========================================================================
1025 # SCM PROPERTIES
1025 # SCM PROPERTIES
1026 #==========================================================================
1026 #==========================================================================
1027
1027
1028 def get_changeset(self, rev=None):
1028 def get_changeset(self, rev=None):
1029 return get_changeset_safe(self.scm_instance, rev)
1029 return get_changeset_safe(self.scm_instance, rev)
1030
1030
1031 def get_landing_changeset(self):
1031 def get_landing_changeset(self):
1032 """
1032 """
1033 Returns landing changeset, or if that doesn't exist returns the tip
1033 Returns landing changeset, or if that doesn't exist returns the tip
1034 """
1034 """
1035 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
1035 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
1036 return cs
1036 return cs
1037
1037
1038 def update_changeset_cache(self, cs_cache=None):
1038 def update_changeset_cache(self, cs_cache=None):
1039 """
1039 """
1040 Update cache of last changeset for repository, keys should be::
1040 Update cache of last changeset for repository, keys should be::
1041
1041
1042 short_id
1042 short_id
1043 raw_id
1043 raw_id
1044 revision
1044 revision
1045 message
1045 message
1046 date
1046 date
1047 author
1047 author
1048
1048
1049 :param cs_cache:
1049 :param cs_cache:
1050 """
1050 """
1051 from rhodecode.lib.vcs.backends.base import BaseChangeset
1051 from rhodecode.lib.vcs.backends.base import BaseChangeset
1052 if cs_cache is None:
1052 if cs_cache is None:
1053 cs_cache = EmptyChangeset()
1053 cs_cache = EmptyChangeset()
1054 # use no-cache version here
1054 # use no-cache version here
1055 scm_repo = self.scm_instance_no_cache()
1055 scm_repo = self.scm_instance_no_cache()
1056 if scm_repo:
1056 if scm_repo:
1057 cs_cache = scm_repo.get_changeset()
1057 cs_cache = scm_repo.get_changeset()
1058
1058
1059 if isinstance(cs_cache, BaseChangeset):
1059 if isinstance(cs_cache, BaseChangeset):
1060 cs_cache = cs_cache.__json__()
1060 cs_cache = cs_cache.__json__()
1061
1061
1062 if (cs_cache != self.changeset_cache or not self.changeset_cache):
1062 if (cs_cache != self.changeset_cache or not self.changeset_cache):
1063 _default = datetime.datetime.fromtimestamp(0)
1063 _default = datetime.datetime.fromtimestamp(0)
1064 last_change = cs_cache.get('date') or _default
1064 last_change = cs_cache.get('date') or _default
1065 log.debug('updated repo %s with new cs cache %s'
1065 log.debug('updated repo %s with new cs cache %s'
1066 % (self.repo_name, cs_cache))
1066 % (self.repo_name, cs_cache))
1067 self.updated_on = last_change
1067 self.updated_on = last_change
1068 self.changeset_cache = cs_cache
1068 self.changeset_cache = cs_cache
1069 Session().add(self)
1069 Session().add(self)
1070 Session().commit()
1070 Session().commit()
1071 else:
1071 else:
1072 log.debug('Skipping repo:%s already with latest changes'
1072 log.debug('Skipping repo:%s already with latest changes'
1073 % self.repo_name)
1073 % self.repo_name)
1074
1074
1075 @property
1075 @property
1076 def tip(self):
1076 def tip(self):
1077 return self.get_changeset('tip')
1077 return self.get_changeset('tip')
1078
1078
1079 @property
1079 @property
1080 def author(self):
1080 def author(self):
1081 return self.tip.author
1081 return self.tip.author
1082
1082
1083 @property
1083 @property
1084 def last_change(self):
1084 def last_change(self):
1085 return self.scm_instance.last_change
1085 return self.scm_instance.last_change
1086
1086
1087 def get_comments(self, revisions=None):
1087 def get_comments(self, revisions=None):
1088 """
1088 """
1089 Returns comments for this repository grouped by revisions
1089 Returns comments for this repository grouped by revisions
1090
1090
1091 :param revisions: filter query by revisions only
1091 :param revisions: filter query by revisions only
1092 """
1092 """
1093 cmts = ChangesetComment.query()\
1093 cmts = ChangesetComment.query()\
1094 .filter(ChangesetComment.repo == self)
1094 .filter(ChangesetComment.repo == self)
1095 if revisions:
1095 if revisions:
1096 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
1096 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
1097 grouped = defaultdict(list)
1097 grouped = defaultdict(list)
1098 for cmt in cmts.all():
1098 for cmt in cmts.all():
1099 grouped[cmt.revision].append(cmt)
1099 grouped[cmt.revision].append(cmt)
1100 return grouped
1100 return grouped
1101
1101
1102 def statuses(self, revisions=None):
1102 def statuses(self, revisions=None):
1103 """
1103 """
1104 Returns statuses for this repository
1104 Returns statuses for this repository
1105
1105
1106 :param revisions: list of revisions to get statuses for
1106 :param revisions: list of revisions to get statuses for
1107 :type revisions: list
1107 :type revisions: list
1108 """
1108 """
1109
1109
1110 statuses = ChangesetStatus.query()\
1110 statuses = ChangesetStatus.query()\
1111 .filter(ChangesetStatus.repo == self)\
1111 .filter(ChangesetStatus.repo == self)\
1112 .filter(ChangesetStatus.version == 0)
1112 .filter(ChangesetStatus.version == 0)
1113 if revisions:
1113 if revisions:
1114 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
1114 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
1115 grouped = {}
1115 grouped = {}
1116
1116
1117 #maybe we have open new pullrequest without a status ?
1117 #maybe we have open new pullrequest without a status ?
1118 stat = ChangesetStatus.STATUS_UNDER_REVIEW
1118 stat = ChangesetStatus.STATUS_UNDER_REVIEW
1119 status_lbl = ChangesetStatus.get_status_lbl(stat)
1119 status_lbl = ChangesetStatus.get_status_lbl(stat)
1120 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
1120 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
1121 for rev in pr.revisions:
1121 for rev in pr.revisions:
1122 pr_id = pr.pull_request_id
1122 pr_id = pr.pull_request_id
1123 pr_repo = pr.other_repo.repo_name
1123 pr_repo = pr.other_repo.repo_name
1124 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
1124 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
1125
1125
1126 for stat in statuses.all():
1126 for stat in statuses.all():
1127 pr_id = pr_repo = None
1127 pr_id = pr_repo = None
1128 if stat.pull_request:
1128 if stat.pull_request:
1129 pr_id = stat.pull_request.pull_request_id
1129 pr_id = stat.pull_request.pull_request_id
1130 pr_repo = stat.pull_request.other_repo.repo_name
1130 pr_repo = stat.pull_request.other_repo.repo_name
1131 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
1131 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
1132 pr_id, pr_repo]
1132 pr_id, pr_repo]
1133 return grouped
1133 return grouped
1134
1134
1135 def _repo_size(self):
1135 def _repo_size(self):
1136 from rhodecode.lib import helpers as h
1136 from rhodecode.lib import helpers as h
1137 log.debug('calculating repository size...')
1137 log.debug('calculating repository size...')
1138 return h.format_byte_size(self.scm_instance.size)
1138 return h.format_byte_size(self.scm_instance.size)
1139
1139
1140 #==========================================================================
1140 #==========================================================================
1141 # SCM CACHE INSTANCE
1141 # SCM CACHE INSTANCE
1142 #==========================================================================
1142 #==========================================================================
1143
1143
1144 @property
1144 @property
1145 def invalidate(self):
1145 def invalidate(self):
1146 return CacheInvalidation.invalidate(self.repo_name)
1146 return CacheInvalidation.invalidate(self.repo_name)
1147
1147
1148 def set_invalidate(self):
1148 def set_invalidate(self):
1149 """
1149 """
1150 set a cache for invalidation for this instance
1150 set a cache for invalidation for this instance
1151 """
1151 """
1152 CacheInvalidation.set_invalidate(repo_name=self.repo_name)
1152 CacheInvalidation.set_invalidate(repo_name=self.repo_name)
1153
1153
1154 def scm_instance_no_cache(self):
1154 def scm_instance_no_cache(self):
1155 return self.__get_instance()
1155 return self.__get_instance()
1156
1156
1157 @LazyProperty
1157 @LazyProperty
1158 def scm_instance(self):
1158 def scm_instance(self):
1159 import rhodecode
1159 import rhodecode
1160 full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
1160 full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
1161 if full_cache:
1161 if full_cache:
1162 return self.scm_instance_cached()
1162 return self.scm_instance_cached()
1163 return self.__get_instance()
1163 return self.__get_instance()
1164
1164
1165 def scm_instance_cached(self, cache_map=None):
1165 def scm_instance_cached(self, cache_map=None):
1166 @cache_region('long_term')
1166 @cache_region('long_term')
1167 def _c(repo_name):
1167 def _c(repo_name):
1168 return self.__get_instance()
1168 return self.__get_instance()
1169 rn = self.repo_name
1169 rn = self.repo_name
1170 log.debug('Getting cached instance of repo')
1170 log.debug('Getting cached instance of repo')
1171
1171
1172 if cache_map:
1172 if cache_map:
1173 # get using prefilled cache_map
1173 # get using prefilled cache_map
1174 invalidate_repo = cache_map[self.repo_name]
1174 invalidate_repo = cache_map[self.repo_name]
1175 if invalidate_repo:
1175 if invalidate_repo:
1176 invalidate_repo = (None if invalidate_repo.cache_active
1176 invalidate_repo = (None if invalidate_repo.cache_active
1177 else invalidate_repo)
1177 else invalidate_repo)
1178 else:
1178 else:
1179 # get from invalidate
1179 # get from invalidate
1180 invalidate_repo = self.invalidate
1180 invalidate_repo = self.invalidate
1181
1181
1182 if invalidate_repo is not None:
1182 if invalidate_repo is not None:
1183 region_invalidate(_c, None, rn)
1183 region_invalidate(_c, None, rn)
1184 # update our cache
1184 # update our cache
1185 CacheInvalidation.set_valid(invalidate_repo.cache_key)
1185 CacheInvalidation.set_valid(invalidate_repo.cache_key)
1186 return _c(rn)
1186 return _c(rn)
1187
1187
1188 def __get_instance(self):
1188 def __get_instance(self):
1189 repo_full_path = self.repo_full_path
1189 repo_full_path = self.repo_full_path
1190 try:
1190 try:
1191 alias = get_scm(repo_full_path)[0]
1191 alias = get_scm(repo_full_path)[0]
1192 log.debug('Creating instance of %s repository from %s'
1192 log.debug('Creating instance of %s repository from %s'
1193 % (alias, repo_full_path))
1193 % (alias, repo_full_path))
1194 backend = get_backend(alias)
1194 backend = get_backend(alias)
1195 except VCSError:
1195 except VCSError:
1196 log.error(traceback.format_exc())
1196 log.error(traceback.format_exc())
1197 log.error('Perhaps this repository is in db and not in '
1197 log.error('Perhaps this repository is in db and not in '
1198 'filesystem run rescan repositories with '
1198 'filesystem run rescan repositories with '
1199 '"destroy old data " option from admin panel')
1199 '"destroy old data " option from admin panel')
1200 return
1200 return
1201
1201
1202 if alias == 'hg':
1202 if alias == 'hg':
1203
1203
1204 repo = backend(safe_str(repo_full_path), create=False,
1204 repo = backend(safe_str(repo_full_path), create=False,
1205 baseui=self._ui)
1205 baseui=self._ui)
1206 # skip hidden web repository
1206 # skip hidden web repository
1207 if repo._get_hidden():
1207 if repo._get_hidden():
1208 return
1208 return
1209 else:
1209 else:
1210 repo = backend(repo_full_path, create=False)
1210 repo = backend(repo_full_path, create=False)
1211
1211
1212 return repo
1212 return repo
1213
1213
1214
1214
1215 class RepoGroup(Base, BaseModel):
1215 class RepoGroup(Base, BaseModel):
1216 __tablename__ = 'groups'
1216 __tablename__ = 'groups'
1217 __table_args__ = (
1217 __table_args__ = (
1218 UniqueConstraint('group_name', 'group_parent_id'),
1218 UniqueConstraint('group_name', 'group_parent_id'),
1219 CheckConstraint('group_id != group_parent_id'),
1219 CheckConstraint('group_id != group_parent_id'),
1220 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1220 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1221 'mysql_charset': 'utf8'},
1221 'mysql_charset': 'utf8'},
1222 )
1222 )
1223 __mapper_args__ = {'order_by': 'group_name'}
1223 __mapper_args__ = {'order_by': 'group_name'}
1224
1224
1225 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1225 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1226 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
1226 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
1227 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
1227 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
1228 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1228 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1229 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
1229 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
1230
1230
1231 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
1231 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
1232 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
1232 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
1233
1233
1234 parent_group = relationship('RepoGroup', remote_side=group_id)
1234 parent_group = relationship('RepoGroup', remote_side=group_id)
1235
1235
1236 def __init__(self, group_name='', parent_group=None):
1236 def __init__(self, group_name='', parent_group=None):
1237 self.group_name = group_name
1237 self.group_name = group_name
1238 self.parent_group = parent_group
1238 self.parent_group = parent_group
1239
1239
1240 def __unicode__(self):
1240 def __unicode__(self):
1241 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
1241 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
1242 self.group_name)
1242 self.group_name)
1243
1243
1244 @classmethod
1244 @classmethod
1245 def groups_choices(cls, groups=None, show_empty_group=True):
1245 def groups_choices(cls, groups=None, show_empty_group=True):
1246 from webhelpers.html import literal as _literal
1246 from webhelpers.html import literal as _literal
1247 if not groups:
1247 if not groups:
1248 groups = cls.query().all()
1248 groups = cls.query().all()
1249
1249
1250 repo_groups = []
1250 repo_groups = []
1251 if show_empty_group:
1251 if show_empty_group:
1252 repo_groups = [('-1', '-- %s --' % _('top level'))]
1252 repo_groups = [('-1', '-- %s --' % _('top level'))]
1253 sep = ' &raquo; '
1253 sep = ' &raquo; '
1254 _name = lambda k: _literal(sep.join(k))
1254 _name = lambda k: _literal(sep.join(k))
1255
1255
1256 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
1256 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
1257 for x in groups])
1257 for x in groups])
1258
1258
1259 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
1259 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
1260 return repo_groups
1260 return repo_groups
1261
1261
1262 @classmethod
1262 @classmethod
1263 def url_sep(cls):
1263 def url_sep(cls):
1264 return URL_SEP
1264 return URL_SEP
1265
1265
1266 @classmethod
1266 @classmethod
1267 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
1267 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
1268 if case_insensitive:
1268 if case_insensitive:
1269 gr = cls.query()\
1269 gr = cls.query()\
1270 .filter(cls.group_name.ilike(group_name))
1270 .filter(cls.group_name.ilike(group_name))
1271 else:
1271 else:
1272 gr = cls.query()\
1272 gr = cls.query()\
1273 .filter(cls.group_name == group_name)
1273 .filter(cls.group_name == group_name)
1274 if cache:
1274 if cache:
1275 gr = gr.options(FromCache(
1275 gr = gr.options(FromCache(
1276 "sql_cache_short",
1276 "sql_cache_short",
1277 "get_group_%s" % _hash_key(group_name)
1277 "get_group_%s" % _hash_key(group_name)
1278 )
1278 )
1279 )
1279 )
1280 return gr.scalar()
1280 return gr.scalar()
1281
1281
1282 @property
1282 @property
1283 def parents(self):
1283 def parents(self):
1284 parents_recursion_limit = 5
1284 parents_recursion_limit = 5
1285 groups = []
1285 groups = []
1286 if self.parent_group is None:
1286 if self.parent_group is None:
1287 return groups
1287 return groups
1288 cur_gr = self.parent_group
1288 cur_gr = self.parent_group
1289 groups.insert(0, cur_gr)
1289 groups.insert(0, cur_gr)
1290 cnt = 0
1290 cnt = 0
1291 while 1:
1291 while 1:
1292 cnt += 1
1292 cnt += 1
1293 gr = getattr(cur_gr, 'parent_group', None)
1293 gr = getattr(cur_gr, 'parent_group', None)
1294 cur_gr = cur_gr.parent_group
1294 cur_gr = cur_gr.parent_group
1295 if gr is None:
1295 if gr is None:
1296 break
1296 break
1297 if cnt == parents_recursion_limit:
1297 if cnt == parents_recursion_limit:
1298 # this will prevent accidental infinit loops
1298 # this will prevent accidental infinit loops
1299 log.error('group nested more than %s' %
1299 log.error('group nested more than %s' %
1300 parents_recursion_limit)
1300 parents_recursion_limit)
1301 break
1301 break
1302
1302
1303 groups.insert(0, gr)
1303 groups.insert(0, gr)
1304 return groups
1304 return groups
1305
1305
1306 @property
1306 @property
1307 def children(self):
1307 def children(self):
1308 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1308 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1309
1309
1310 @property
1310 @property
1311 def name(self):
1311 def name(self):
1312 return self.group_name.split(RepoGroup.url_sep())[-1]
1312 return self.group_name.split(RepoGroup.url_sep())[-1]
1313
1313
1314 @property
1314 @property
1315 def full_path(self):
1315 def full_path(self):
1316 return self.group_name
1316 return self.group_name
1317
1317
1318 @property
1318 @property
1319 def full_path_splitted(self):
1319 def full_path_splitted(self):
1320 return self.group_name.split(RepoGroup.url_sep())
1320 return self.group_name.split(RepoGroup.url_sep())
1321
1321
1322 @property
1322 @property
1323 def repositories(self):
1323 def repositories(self):
1324 return Repository.query()\
1324 return Repository.query()\
1325 .filter(Repository.group == self)\
1325 .filter(Repository.group == self)\
1326 .order_by(Repository.repo_name)
1326 .order_by(Repository.repo_name)
1327
1327
1328 @property
1328 @property
1329 def repositories_recursive_count(self):
1329 def repositories_recursive_count(self):
1330 cnt = self.repositories.count()
1330 cnt = self.repositories.count()
1331
1331
1332 def children_count(group):
1332 def children_count(group):
1333 cnt = 0
1333 cnt = 0
1334 for child in group.children:
1334 for child in group.children:
1335 cnt += child.repositories.count()
1335 cnt += child.repositories.count()
1336 cnt += children_count(child)
1336 cnt += children_count(child)
1337 return cnt
1337 return cnt
1338
1338
1339 return cnt + children_count(self)
1339 return cnt + children_count(self)
1340
1340
1341 def _recursive_objects(self, include_repos=True):
1341 def _recursive_objects(self, include_repos=True):
1342 all_ = []
1342 all_ = []
1343
1343
1344 def _get_members(root_gr):
1344 def _get_members(root_gr):
1345 if include_repos:
1345 if include_repos:
1346 for r in root_gr.repositories:
1346 for r in root_gr.repositories:
1347 all_.append(r)
1347 all_.append(r)
1348 childs = root_gr.children.all()
1348 childs = root_gr.children.all()
1349 if childs:
1349 if childs:
1350 for gr in childs:
1350 for gr in childs:
1351 all_.append(gr)
1351 all_.append(gr)
1352 _get_members(gr)
1352 _get_members(gr)
1353
1353
1354 _get_members(self)
1354 _get_members(self)
1355 return [self] + all_
1355 return [self] + all_
1356
1356
1357 def recursive_groups_and_repos(self):
1357 def recursive_groups_and_repos(self):
1358 """
1358 """
1359 Recursive return all groups, with repositories in those groups
1359 Recursive return all groups, with repositories in those groups
1360 """
1360 """
1361 return self._recursive_objects()
1361 return self._recursive_objects()
1362
1362
1363 def recursive_groups(self):
1363 def recursive_groups(self):
1364 """
1364 """
1365 Returns all children groups for this group including children of children
1365 Returns all children groups for this group including children of children
1366 """
1366 """
1367 return self._recursive_objects(include_repos=False)
1367 return self._recursive_objects(include_repos=False)
1368
1368
1369 def get_new_name(self, group_name):
1369 def get_new_name(self, group_name):
1370 """
1370 """
1371 returns new full group name based on parent and new name
1371 returns new full group name based on parent and new name
1372
1372
1373 :param group_name:
1373 :param group_name:
1374 """
1374 """
1375 path_prefix = (self.parent_group.full_path_splitted if
1375 path_prefix = (self.parent_group.full_path_splitted if
1376 self.parent_group else [])
1376 self.parent_group else [])
1377 return RepoGroup.url_sep().join(path_prefix + [group_name])
1377 return RepoGroup.url_sep().join(path_prefix + [group_name])
1378
1378
1379
1379
1380 class Permission(Base, BaseModel):
1380 class Permission(Base, BaseModel):
1381 __tablename__ = 'permissions'
1381 __tablename__ = 'permissions'
1382 __table_args__ = (
1382 __table_args__ = (
1383 Index('p_perm_name_idx', 'permission_name'),
1383 Index('p_perm_name_idx', 'permission_name'),
1384 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1384 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1385 'mysql_charset': 'utf8'},
1385 'mysql_charset': 'utf8'},
1386 )
1386 )
1387 PERMS = [
1387 PERMS = [
1388 ('repository.none', _('Repository no access')),
1388 ('repository.none', _('Repository no access')),
1389 ('repository.read', _('Repository read access')),
1389 ('repository.read', _('Repository read access')),
1390 ('repository.write', _('Repository write access')),
1390 ('repository.write', _('Repository write access')),
1391 ('repository.admin', _('Repository admin access')),
1391 ('repository.admin', _('Repository admin access')),
1392
1392
1393 ('group.none', _('Repository group no access')),
1393 ('group.none', _('Repository group no access')),
1394 ('group.read', _('Repository group read access')),
1394 ('group.read', _('Repository group read access')),
1395 ('group.write', _('Repository group write access')),
1395 ('group.write', _('Repository group write access')),
1396 ('group.admin', _('Repository group admin access')),
1396 ('group.admin', _('Repository group admin access')),
1397
1397
1398 ('hg.admin', _('RhodeCode Administrator')),
1398 ('hg.admin', _('RhodeCode Administrator')),
1399 ('hg.create.none', _('Repository creation disabled')),
1399 ('hg.create.none', _('Repository creation disabled')),
1400 ('hg.create.repository', _('Repository creation enabled')),
1400 ('hg.create.repository', _('Repository creation enabled')),
1401 ('hg.fork.none', _('Repository forking disabled')),
1401 ('hg.fork.none', _('Repository forking disabled')),
1402 ('hg.fork.repository', _('Repository forking enabled')),
1402 ('hg.fork.repository', _('Repository forking enabled')),
1403 ('hg.register.none', _('Register disabled')),
1403 ('hg.register.none', _('Register disabled')),
1404 ('hg.register.manual_activate', _('Register new user with RhodeCode '
1404 ('hg.register.manual_activate', _('Register new user with RhodeCode '
1405 'with manual activation')),
1405 'with manual activation')),
1406
1406
1407 ('hg.register.auto_activate', _('Register new user with RhodeCode '
1407 ('hg.register.auto_activate', _('Register new user with RhodeCode '
1408 'with auto activation')),
1408 'with auto activation')),
1409 ]
1409 ]
1410
1410
1411 # defines which permissions are more important higher the more important
1411 # defines which permissions are more important higher the more important
1412 PERM_WEIGHTS = {
1412 PERM_WEIGHTS = {
1413 'repository.none': 0,
1413 'repository.none': 0,
1414 'repository.read': 1,
1414 'repository.read': 1,
1415 'repository.write': 3,
1415 'repository.write': 3,
1416 'repository.admin': 4,
1416 'repository.admin': 4,
1417
1417
1418 'group.none': 0,
1418 'group.none': 0,
1419 'group.read': 1,
1419 'group.read': 1,
1420 'group.write': 3,
1420 'group.write': 3,
1421 'group.admin': 4,
1421 'group.admin': 4,
1422
1422
1423 'hg.fork.none': 0,
1423 'hg.fork.none': 0,
1424 'hg.fork.repository': 1,
1424 'hg.fork.repository': 1,
1425 'hg.create.none': 0,
1425 'hg.create.none': 0,
1426 'hg.create.repository':1
1426 'hg.create.repository':1
1427 }
1427 }
1428
1428
1429 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1429 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1430 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1430 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1431 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1431 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1432
1432
1433 def __unicode__(self):
1433 def __unicode__(self):
1434 return u"<%s('%s:%s')>" % (
1434 return u"<%s('%s:%s')>" % (
1435 self.__class__.__name__, self.permission_id, self.permission_name
1435 self.__class__.__name__, self.permission_id, self.permission_name
1436 )
1436 )
1437
1437
1438 @classmethod
1438 @classmethod
1439 def get_by_key(cls, key):
1439 def get_by_key(cls, key):
1440 return cls.query().filter(cls.permission_name == key).scalar()
1440 return cls.query().filter(cls.permission_name == key).scalar()
1441
1441
1442 @classmethod
1442 @classmethod
1443 def get_default_perms(cls, default_user_id):
1443 def get_default_perms(cls, default_user_id):
1444 q = Session().query(UserRepoToPerm, Repository, cls)\
1444 q = Session().query(UserRepoToPerm, Repository, cls)\
1445 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1445 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1446 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1446 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1447 .filter(UserRepoToPerm.user_id == default_user_id)
1447 .filter(UserRepoToPerm.user_id == default_user_id)
1448
1448
1449 return q.all()
1449 return q.all()
1450
1450
1451 @classmethod
1451 @classmethod
1452 def get_default_group_perms(cls, default_user_id):
1452 def get_default_group_perms(cls, default_user_id):
1453 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1453 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1454 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1454 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1455 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1455 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1456 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1456 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1457
1457
1458 return q.all()
1458 return q.all()
1459
1459
1460
1460
1461 class UserRepoToPerm(Base, BaseModel):
1461 class UserRepoToPerm(Base, BaseModel):
1462 __tablename__ = 'repo_to_perm'
1462 __tablename__ = 'repo_to_perm'
1463 __table_args__ = (
1463 __table_args__ = (
1464 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1464 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1465 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1465 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1466 'mysql_charset': 'utf8'}
1466 'mysql_charset': 'utf8'}
1467 )
1467 )
1468 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1468 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1469 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1469 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1470 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1470 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1471 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1471 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1472
1472
1473 user = relationship('User')
1473 user = relationship('User')
1474 repository = relationship('Repository')
1474 repository = relationship('Repository')
1475 permission = relationship('Permission')
1475 permission = relationship('Permission')
1476
1476
1477 @classmethod
1477 @classmethod
1478 def create(cls, user, repository, permission):
1478 def create(cls, user, repository, permission):
1479 n = cls()
1479 n = cls()
1480 n.user = user
1480 n.user = user
1481 n.repository = repository
1481 n.repository = repository
1482 n.permission = permission
1482 n.permission = permission
1483 Session().add(n)
1483 Session().add(n)
1484 return n
1484 return n
1485
1485
1486 def __unicode__(self):
1486 def __unicode__(self):
1487 return u'<user:%s => %s >' % (self.user, self.repository)
1487 return u'<user:%s => %s >' % (self.user, self.repository)
1488
1488
1489
1489
1490 class UserToPerm(Base, BaseModel):
1490 class UserToPerm(Base, BaseModel):
1491 __tablename__ = 'user_to_perm'
1491 __tablename__ = 'user_to_perm'
1492 __table_args__ = (
1492 __table_args__ = (
1493 UniqueConstraint('user_id', 'permission_id'),
1493 UniqueConstraint('user_id', 'permission_id'),
1494 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1494 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1495 'mysql_charset': 'utf8'}
1495 'mysql_charset': 'utf8'}
1496 )
1496 )
1497 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1497 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1498 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1498 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1499 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1499 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1500
1500
1501 user = relationship('User')
1501 user = relationship('User')
1502 permission = relationship('Permission', lazy='joined')
1502 permission = relationship('Permission', lazy='joined')
1503
1503
1504
1504
1505 class UserGroupRepoToPerm(Base, BaseModel):
1505 class UserGroupRepoToPerm(Base, BaseModel):
1506 __tablename__ = 'users_group_repo_to_perm'
1506 __tablename__ = 'users_group_repo_to_perm'
1507 __table_args__ = (
1507 __table_args__ = (
1508 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1508 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1509 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1509 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1510 'mysql_charset': 'utf8'}
1510 'mysql_charset': 'utf8'}
1511 )
1511 )
1512 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1512 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1513 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1513 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1514 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1514 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1515 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1515 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1516
1516
1517 users_group = relationship('UserGroup')
1517 users_group = relationship('UserGroup')
1518 permission = relationship('Permission')
1518 permission = relationship('Permission')
1519 repository = relationship('Repository')
1519 repository = relationship('Repository')
1520
1520
1521 @classmethod
1521 @classmethod
1522 def create(cls, users_group, repository, permission):
1522 def create(cls, users_group, repository, permission):
1523 n = cls()
1523 n = cls()
1524 n.users_group = users_group
1524 n.users_group = users_group
1525 n.repository = repository
1525 n.repository = repository
1526 n.permission = permission
1526 n.permission = permission
1527 Session().add(n)
1527 Session().add(n)
1528 return n
1528 return n
1529
1529
1530 def __unicode__(self):
1530 def __unicode__(self):
1531 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1531 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1532
1532
1533
1533
1534 class UserGroupToPerm(Base, BaseModel):
1534 class UserGroupToPerm(Base, BaseModel):
1535 __tablename__ = 'users_group_to_perm'
1535 __tablename__ = 'users_group_to_perm'
1536 __table_args__ = (
1536 __table_args__ = (
1537 UniqueConstraint('users_group_id', 'permission_id',),
1537 UniqueConstraint('users_group_id', 'permission_id',),
1538 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1538 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1539 'mysql_charset': 'utf8'}
1539 'mysql_charset': 'utf8'}
1540 )
1540 )
1541 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1541 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1542 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1542 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1543 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1543 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1544
1544
1545 users_group = relationship('UserGroup')
1545 users_group = relationship('UserGroup')
1546 permission = relationship('Permission')
1546 permission = relationship('Permission')
1547
1547
1548
1548
1549 class UserRepoGroupToPerm(Base, BaseModel):
1549 class UserRepoGroupToPerm(Base, BaseModel):
1550 __tablename__ = 'user_repo_group_to_perm'
1550 __tablename__ = 'user_repo_group_to_perm'
1551 __table_args__ = (
1551 __table_args__ = (
1552 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1552 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1553 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1553 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1554 'mysql_charset': 'utf8'}
1554 'mysql_charset': 'utf8'}
1555 )
1555 )
1556
1556
1557 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1557 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1558 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1558 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1559 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1559 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1560 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1560 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1561
1561
1562 user = relationship('User')
1562 user = relationship('User')
1563 group = relationship('RepoGroup')
1563 group = relationship('RepoGroup')
1564 permission = relationship('Permission')
1564 permission = relationship('Permission')
1565
1565
1566
1566
1567 class UserGroupRepoGroupToPerm(Base, BaseModel):
1567 class UserGroupRepoGroupToPerm(Base, BaseModel):
1568 __tablename__ = 'users_group_repo_group_to_perm'
1568 __tablename__ = 'users_group_repo_group_to_perm'
1569 __table_args__ = (
1569 __table_args__ = (
1570 UniqueConstraint('users_group_id', 'group_id'),
1570 UniqueConstraint('users_group_id', 'group_id'),
1571 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1571 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1572 'mysql_charset': 'utf8'}
1572 'mysql_charset': 'utf8'}
1573 )
1573 )
1574
1574
1575 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)
1575 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)
1576 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1576 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1577 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1577 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1578 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1578 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1579
1579
1580 users_group = relationship('UserGroup')
1580 users_group = relationship('UserGroup')
1581 permission = relationship('Permission')
1581 permission = relationship('Permission')
1582 group = relationship('RepoGroup')
1582 group = relationship('RepoGroup')
1583
1583
1584
1584
1585 class Statistics(Base, BaseModel):
1585 class Statistics(Base, BaseModel):
1586 __tablename__ = 'statistics'
1586 __tablename__ = 'statistics'
1587 __table_args__ = (
1587 __table_args__ = (
1588 UniqueConstraint('repository_id'),
1588 UniqueConstraint('repository_id'),
1589 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1589 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1590 'mysql_charset': 'utf8'}
1590 'mysql_charset': 'utf8'}
1591 )
1591 )
1592 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1592 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1593 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1593 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1594 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1594 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1595 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1595 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1596 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1596 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1597 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1597 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1598
1598
1599 repository = relationship('Repository', single_parent=True)
1599 repository = relationship('Repository', single_parent=True)
1600
1600
1601
1601
1602 class UserFollowing(Base, BaseModel):
1602 class UserFollowing(Base, BaseModel):
1603 __tablename__ = 'user_followings'
1603 __tablename__ = 'user_followings'
1604 __table_args__ = (
1604 __table_args__ = (
1605 UniqueConstraint('user_id', 'follows_repository_id'),
1605 UniqueConstraint('user_id', 'follows_repository_id'),
1606 UniqueConstraint('user_id', 'follows_user_id'),
1606 UniqueConstraint('user_id', 'follows_user_id'),
1607 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1607 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1608 'mysql_charset': 'utf8'}
1608 'mysql_charset': 'utf8'}
1609 )
1609 )
1610
1610
1611 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1611 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1612 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1612 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1613 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1613 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1614 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1614 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1615 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1615 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1616
1616
1617 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1617 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1618
1618
1619 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1619 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1620 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1620 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1621
1621
1622 @classmethod
1622 @classmethod
1623 def get_repo_followers(cls, repo_id):
1623 def get_repo_followers(cls, repo_id):
1624 return cls.query().filter(cls.follows_repo_id == repo_id)
1624 return cls.query().filter(cls.follows_repo_id == repo_id)
1625
1625
1626
1626
1627 class CacheInvalidation(Base, BaseModel):
1627 class CacheInvalidation(Base, BaseModel):
1628 __tablename__ = 'cache_invalidation'
1628 __tablename__ = 'cache_invalidation'
1629 __table_args__ = (
1629 __table_args__ = (
1630 UniqueConstraint('cache_key'),
1630 UniqueConstraint('cache_key'),
1631 Index('key_idx', 'cache_key'),
1631 Index('key_idx', 'cache_key'),
1632 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1632 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1633 'mysql_charset': 'utf8'},
1633 'mysql_charset': 'utf8'},
1634 )
1634 )
1635 # cache_id, not used
1635 # cache_id, not used
1636 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1636 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1637 # cache_key as created by _get_cache_key
1637 # cache_key as created by _get_cache_key
1638 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1638 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1639 # cache_args is usually a repo_name, possibly with _README/_RSS/_ATOM suffix
1639 # cache_args is usually a repo_name, possibly with _README/_RSS/_ATOM suffix
1640 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1640 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1641 # instance sets cache_active True when it is caching, other instances set cache_active to False to invalidate
1641 # instance sets cache_active True when it is caching, other instances set cache_active to False to invalidate
1642 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1642 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1643
1643
1644 def __init__(self, cache_key, cache_args=''):
1644 def __init__(self, cache_key, cache_args=''):
1645 self.cache_key = cache_key
1645 self.cache_key = cache_key
1646 self.cache_args = cache_args
1646 self.cache_args = cache_args
1647 self.cache_active = False
1647 self.cache_active = False
1648
1648
1649 def __unicode__(self):
1649 def __unicode__(self):
1650 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1650 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1651 self.cache_id, self.cache_key)
1651 self.cache_id, self.cache_key)
1652
1652
1653 def get_prefix(self):
1653 def get_prefix(self):
1654 """
1654 """
1655 Guess prefix that might have been used in _get_cache_key to generate self.cache_key .
1655 Guess prefix that might have been used in _get_cache_key to generate self.cache_key .
1656 Only used for informational purposes in repo_edit.html .
1656 Only used for informational purposes in repo_edit.html .
1657 """
1657 """
1658 _split = self.cache_key.split(self.cache_args, 1)
1658 _split = self.cache_key.split(self.cache_args, 1)
1659 if len(_split) == 2:
1659 if len(_split) == 2:
1660 return _split[0]
1660 return _split[0]
1661 return ''
1661 return ''
1662
1662
1663 @classmethod
1663 @classmethod
1664 def _get_cache_key(cls, key):
1664 def _get_cache_key(cls, key):
1665 """
1665 """
1666 Wrapper for generating a unique cache key for this instance and "key".
1666 Wrapper for generating a unique cache key for this instance and "key".
1667 """
1667 """
1668 import rhodecode
1668 import rhodecode
1669 prefix = rhodecode.CONFIG.get('instance_id', '')
1669 prefix = rhodecode.CONFIG.get('instance_id', '')
1670 return "%s%s" % (prefix, key)
1670 return "%s%s" % (prefix, key)
1671
1671
1672 @classmethod
1672 @classmethod
1673 def _get_or_create_inv_obj(cls, key, repo_name, commit=True):
1673 def _get_or_create_inv_obj(cls, key, repo_name, commit=True):
1674 inv_obj = Session().query(cls).filter(cls.cache_key == key).scalar()
1674 inv_obj = Session().query(cls).filter(cls.cache_key == key).scalar()
1675 if not inv_obj:
1675 if not inv_obj:
1676 try:
1676 try:
1677 inv_obj = CacheInvalidation(key, repo_name)
1677 inv_obj = CacheInvalidation(key, repo_name)
1678 Session().add(inv_obj)
1678 Session().add(inv_obj)
1679 if commit:
1679 if commit:
1680 Session().commit()
1680 Session().commit()
1681 except Exception:
1681 except Exception:
1682 log.error(traceback.format_exc())
1682 log.error(traceback.format_exc())
1683 Session().rollback()
1683 Session().rollback()
1684 return inv_obj
1684 return inv_obj
1685
1685
1686 @classmethod
1686 @classmethod
1687 def invalidate(cls, key):
1687 def invalidate(cls, key):
1688 """
1688 """
1689 Returns Invalidation object if this given key should be invalidated
1689 Returns Invalidation object if this given key should be invalidated
1690 None otherwise. `cache_active = False` means that this cache
1690 None otherwise. `cache_active = False` means that this cache
1691 state is not valid and needs to be invalidated
1691 state is not valid and needs to be invalidated
1692
1692
1693 :param key:
1693 :param key:
1694 """
1694 """
1695 repo_name = key
1695 repo_name = key
1696 repo_name = remove_suffix(repo_name, '_README')
1696 repo_name = remove_suffix(repo_name, '_README')
1697 repo_name = remove_suffix(repo_name, '_RSS')
1697 repo_name = remove_suffix(repo_name, '_RSS')
1698 repo_name = remove_suffix(repo_name, '_ATOM')
1698 repo_name = remove_suffix(repo_name, '_ATOM')
1699
1699
1700 cache_key = cls._get_cache_key(key)
1700 cache_key = cls._get_cache_key(key)
1701 inv = cls._get_or_create_inv_obj(cache_key, repo_name)
1701 inv = cls._get_or_create_inv_obj(cache_key, repo_name)
1702
1702
1703 if inv and not inv.cache_active:
1703 if inv and not inv.cache_active:
1704 return inv
1704 return inv
1705
1705
1706 @classmethod
1706 @classmethod
1707 def set_invalidate(cls, key=None, repo_name=None):
1707 def set_invalidate(cls, key=None, repo_name=None):
1708 """
1708 """
1709 Mark this Cache key for invalidation, either by key or whole
1709 Mark this Cache key for invalidation, either by key or whole
1710 cache sets based on repo_name
1710 cache sets based on repo_name
1711
1711
1712 :param key:
1712 :param key:
1713 """
1713 """
1714 invalidated_keys = []
1714 invalidated_keys = []
1715 if key:
1715 if key:
1716 assert not repo_name
1716 assert not repo_name
1717 cache_key = cls._get_cache_key(key)
1717 cache_key = cls._get_cache_key(key)
1718 inv_objs = Session().query(cls).filter(cls.cache_key == cache_key).all()
1718 inv_objs = Session().query(cls).filter(cls.cache_key == cache_key).all()
1719 else:
1719 else:
1720 assert repo_name
1720 assert repo_name
1721 inv_objs = Session().query(cls).filter(cls.cache_args == repo_name).all()
1721 inv_objs = Session().query(cls).filter(cls.cache_args == repo_name).all()
1722
1722
1723 try:
1723 try:
1724 for inv_obj in inv_objs:
1724 for inv_obj in inv_objs:
1725 inv_obj.cache_active = False
1725 inv_obj.cache_active = False
1726 log.debug('marking %s key for invalidation based on key=%s,repo_name=%s'
1726 log.debug('marking %s key for invalidation based on key=%s,repo_name=%s'
1727 % (inv_obj, key, safe_str(repo_name)))
1727 % (inv_obj, key, safe_str(repo_name)))
1728 invalidated_keys.append(inv_obj.cache_key)
1728 invalidated_keys.append(inv_obj.cache_key)
1729 Session().add(inv_obj)
1729 Session().add(inv_obj)
1730 Session().commit()
1730 Session().commit()
1731 except Exception:
1731 except Exception:
1732 log.error(traceback.format_exc())
1732 log.error(traceback.format_exc())
1733 Session().rollback()
1733 Session().rollback()
1734 return invalidated_keys
1734 return invalidated_keys
1735
1735
1736 @classmethod
1736 @classmethod
1737 def set_valid(cls, key):
1737 def set_valid(cls, key):
1738 """
1738 """
1739 Mark this cache key as active and currently cached
1739 Mark this cache key as active and currently cached
1740
1740
1741 :param key:
1741 :param key:
1742 """
1742 """
1743 inv_obj = cls.query().filter(cls.cache_key == key).scalar()
1743 inv_obj = cls.query().filter(cls.cache_key == key).scalar()
1744 inv_obj.cache_active = True
1744 inv_obj.cache_active = True
1745 Session().add(inv_obj)
1745 Session().add(inv_obj)
1746 Session().commit()
1746 Session().commit()
1747
1747
1748 @classmethod
1748 @classmethod
1749 def get_cache_map(cls):
1749 def get_cache_map(cls):
1750
1750
1751 class cachemapdict(dict):
1751 class cachemapdict(dict):
1752
1752
1753 def __init__(self, *args, **kwargs):
1753 def __init__(self, *args, **kwargs):
1754 self.fixkey = kwargs.pop('fixkey', False)
1754 self.fixkey = kwargs.pop('fixkey', False)
1755 super(cachemapdict, self).__init__(*args, **kwargs)
1755 super(cachemapdict, self).__init__(*args, **kwargs)
1756
1756
1757 def __getattr__(self, name):
1757 def __getattr__(self, name):
1758 cache_key = name
1758 cache_key = name
1759 if self.fixkey:
1759 if self.fixkey:
1760 cache_key = cls._get_cache_key(name)
1760 cache_key = cls._get_cache_key(name)
1761 if cache_key in self.__dict__:
1761 if cache_key in self.__dict__:
1762 return self.__dict__[cache_key]
1762 return self.__dict__[cache_key]
1763 else:
1763 else:
1764 return self[cache_key]
1764 return self[cache_key]
1765
1765
1766 def __getitem__(self, name):
1766 def __getitem__(self, name):
1767 cache_key = name
1767 cache_key = name
1768 if self.fixkey:
1768 if self.fixkey:
1769 cache_key = cls._get_cache_key(name)
1769 cache_key = cls._get_cache_key(name)
1770 try:
1770 try:
1771 return super(cachemapdict, self).__getitem__(cache_key)
1771 return super(cachemapdict, self).__getitem__(cache_key)
1772 except KeyError:
1772 except KeyError:
1773 return None
1773 return None
1774
1774
1775 cache_map = cachemapdict(fixkey=True)
1775 cache_map = cachemapdict(fixkey=True)
1776 for obj in cls.query().all():
1776 for obj in cls.query().all():
1777 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1777 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1778 return cache_map
1778 return cache_map
1779
1779
1780
1780
1781 class ChangesetComment(Base, BaseModel):
1781 class ChangesetComment(Base, BaseModel):
1782 __tablename__ = 'changeset_comments'
1782 __tablename__ = 'changeset_comments'
1783 __table_args__ = (
1783 __table_args__ = (
1784 Index('cc_revision_idx', 'revision'),
1784 Index('cc_revision_idx', 'revision'),
1785 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1785 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1786 'mysql_charset': 'utf8'},
1786 'mysql_charset': 'utf8'},
1787 )
1787 )
1788 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1788 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1789 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1789 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1790 revision = Column('revision', String(40), nullable=True)
1790 revision = Column('revision', String(40), nullable=True)
1791 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1791 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1792 line_no = Column('line_no', Unicode(10), nullable=True)
1792 line_no = Column('line_no', Unicode(10), nullable=True)
1793 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1793 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1794 f_path = Column('f_path', Unicode(1000), nullable=True)
1794 f_path = Column('f_path', Unicode(1000), nullable=True)
1795 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1795 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1796 text = Column('text', UnicodeText(25000), nullable=False)
1796 text = Column('text', UnicodeText(25000), nullable=False)
1797 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1797 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1798 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1798 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1799
1799
1800 author = relationship('User', lazy='joined')
1800 author = relationship('User', lazy='joined')
1801 repo = relationship('Repository')
1801 repo = relationship('Repository')
1802 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1802 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1803 pull_request = relationship('PullRequest', lazy='joined')
1803 pull_request = relationship('PullRequest', lazy='joined')
1804
1804
1805 @classmethod
1805 @classmethod
1806 def get_users(cls, revision=None, pull_request_id=None):
1806 def get_users(cls, revision=None, pull_request_id=None):
1807 """
1807 """
1808 Returns user associated with this ChangesetComment. ie those
1808 Returns user associated with this ChangesetComment. ie those
1809 who actually commented
1809 who actually commented
1810
1810
1811 :param cls:
1811 :param cls:
1812 :param revision:
1812 :param revision:
1813 """
1813 """
1814 q = Session().query(User)\
1814 q = Session().query(User)\
1815 .join(ChangesetComment.author)
1815 .join(ChangesetComment.author)
1816 if revision:
1816 if revision:
1817 q = q.filter(cls.revision == revision)
1817 q = q.filter(cls.revision == revision)
1818 elif pull_request_id:
1818 elif pull_request_id:
1819 q = q.filter(cls.pull_request_id == pull_request_id)
1819 q = q.filter(cls.pull_request_id == pull_request_id)
1820 return q.all()
1820 return q.all()
1821
1821
1822
1822
1823 class ChangesetStatus(Base, BaseModel):
1823 class ChangesetStatus(Base, BaseModel):
1824 __tablename__ = 'changeset_statuses'
1824 __tablename__ = 'changeset_statuses'
1825 __table_args__ = (
1825 __table_args__ = (
1826 Index('cs_revision_idx', 'revision'),
1826 Index('cs_revision_idx', 'revision'),
1827 Index('cs_version_idx', 'version'),
1827 Index('cs_version_idx', 'version'),
1828 UniqueConstraint('repo_id', 'revision', 'version'),
1828 UniqueConstraint('repo_id', 'revision', 'version'),
1829 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1829 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1830 'mysql_charset': 'utf8'}
1830 'mysql_charset': 'utf8'}
1831 )
1831 )
1832 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1832 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1833 STATUS_APPROVED = 'approved'
1833 STATUS_APPROVED = 'approved'
1834 STATUS_REJECTED = 'rejected'
1834 STATUS_REJECTED = 'rejected'
1835 STATUS_UNDER_REVIEW = 'under_review'
1835 STATUS_UNDER_REVIEW = 'under_review'
1836
1836
1837 STATUSES = [
1837 STATUSES = [
1838 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1838 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1839 (STATUS_APPROVED, _("Approved")),
1839 (STATUS_APPROVED, _("Approved")),
1840 (STATUS_REJECTED, _("Rejected")),
1840 (STATUS_REJECTED, _("Rejected")),
1841 (STATUS_UNDER_REVIEW, _("Under Review")),
1841 (STATUS_UNDER_REVIEW, _("Under Review")),
1842 ]
1842 ]
1843
1843
1844 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1844 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1845 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1845 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1846 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1846 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1847 revision = Column('revision', String(40), nullable=False)
1847 revision = Column('revision', String(40), nullable=False)
1848 status = Column('status', String(128), nullable=False, default=DEFAULT)
1848 status = Column('status', String(128), nullable=False, default=DEFAULT)
1849 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1849 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1850 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1850 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1851 version = Column('version', Integer(), nullable=False, default=0)
1851 version = Column('version', Integer(), nullable=False, default=0)
1852 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1852 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1853
1853
1854 author = relationship('User', lazy='joined')
1854 author = relationship('User', lazy='joined')
1855 repo = relationship('Repository')
1855 repo = relationship('Repository')
1856 comment = relationship('ChangesetComment', lazy='joined')
1856 comment = relationship('ChangesetComment', lazy='joined')
1857 pull_request = relationship('PullRequest', lazy='joined')
1857 pull_request = relationship('PullRequest', lazy='joined')
1858
1858
1859 def __unicode__(self):
1859 def __unicode__(self):
1860 return u"<%s('%s:%s')>" % (
1860 return u"<%s('%s:%s')>" % (
1861 self.__class__.__name__,
1861 self.__class__.__name__,
1862 self.status, self.author
1862 self.status, self.author
1863 )
1863 )
1864
1864
1865 @classmethod
1865 @classmethod
1866 def get_status_lbl(cls, value):
1866 def get_status_lbl(cls, value):
1867 return dict(cls.STATUSES).get(value)
1867 return dict(cls.STATUSES).get(value)
1868
1868
1869 @property
1869 @property
1870 def status_lbl(self):
1870 def status_lbl(self):
1871 return ChangesetStatus.get_status_lbl(self.status)
1871 return ChangesetStatus.get_status_lbl(self.status)
1872
1872
1873
1873
1874 class PullRequest(Base, BaseModel):
1874 class PullRequest(Base, BaseModel):
1875 __tablename__ = 'pull_requests'
1875 __tablename__ = 'pull_requests'
1876 __table_args__ = (
1876 __table_args__ = (
1877 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1877 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1878 'mysql_charset': 'utf8'},
1878 'mysql_charset': 'utf8'},
1879 )
1879 )
1880
1880
1881 STATUS_NEW = u'new'
1881 STATUS_NEW = u'new'
1882 STATUS_OPEN = u'open'
1882 STATUS_OPEN = u'open'
1883 STATUS_CLOSED = u'closed'
1883 STATUS_CLOSED = u'closed'
1884
1884
1885 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1885 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1886 title = Column('title', Unicode(256), nullable=True)
1886 title = Column('title', Unicode(256), nullable=True)
1887 description = Column('description', UnicodeText(10240), nullable=True)
1887 description = Column('description', UnicodeText(10240), nullable=True)
1888 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1888 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1889 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1889 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1890 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1890 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1891 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1891 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1892 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1892 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1893 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1893 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1894 org_ref = Column('org_ref', Unicode(256), nullable=False)
1894 org_ref = Column('org_ref', Unicode(256), nullable=False)
1895 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1895 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1896 other_ref = Column('other_ref', Unicode(256), nullable=False)
1896 other_ref = Column('other_ref', Unicode(256), nullable=False)
1897
1897
1898 @hybrid_property
1898 @hybrid_property
1899 def revisions(self):
1899 def revisions(self):
1900 return self._revisions.split(':')
1900 return self._revisions.split(':')
1901
1901
1902 @revisions.setter
1902 @revisions.setter
1903 def revisions(self, val):
1903 def revisions(self, val):
1904 self._revisions = ':'.join(val)
1904 self._revisions = ':'.join(val)
1905
1905
1906 @property
1906 @property
1907 def org_ref_parts(self):
1907 def org_ref_parts(self):
1908 return self.org_ref.split(':')
1908 return self.org_ref.split(':')
1909
1909
1910 @property
1910 @property
1911 def other_ref_parts(self):
1911 def other_ref_parts(self):
1912 return self.other_ref.split(':')
1912 return self.other_ref.split(':')
1913
1913
1914 author = relationship('User', lazy='joined')
1914 author = relationship('User', lazy='joined')
1915 reviewers = relationship('PullRequestReviewers',
1915 reviewers = relationship('PullRequestReviewers',
1916 cascade="all, delete, delete-orphan")
1916 cascade="all, delete, delete-orphan")
1917 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1917 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1918 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1918 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1919 statuses = relationship('ChangesetStatus')
1919 statuses = relationship('ChangesetStatus')
1920 comments = relationship('ChangesetComment',
1920 comments = relationship('ChangesetComment',
1921 cascade="all, delete, delete-orphan")
1921 cascade="all, delete, delete-orphan")
1922
1922
1923 def is_closed(self):
1923 def is_closed(self):
1924 return self.status == self.STATUS_CLOSED
1924 return self.status == self.STATUS_CLOSED
1925
1925
1926 @property
1926 @property
1927 def last_review_status(self):
1927 def last_review_status(self):
1928 return self.statuses[-1].status if self.statuses else ''
1928 return self.statuses[-1].status if self.statuses else ''
1929
1929
1930 def __json__(self):
1930 def __json__(self):
1931 return dict(
1931 return dict(
1932 revisions=self.revisions
1932 revisions=self.revisions
1933 )
1933 )
1934
1934
1935
1935
1936 class PullRequestReviewers(Base, BaseModel):
1936 class PullRequestReviewers(Base, BaseModel):
1937 __tablename__ = 'pull_request_reviewers'
1937 __tablename__ = 'pull_request_reviewers'
1938 __table_args__ = (
1938 __table_args__ = (
1939 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1939 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1940 'mysql_charset': 'utf8'},
1940 'mysql_charset': 'utf8'},
1941 )
1941 )
1942
1942
1943 def __init__(self, user=None, pull_request=None):
1943 def __init__(self, user=None, pull_request=None):
1944 self.user = user
1944 self.user = user
1945 self.pull_request = pull_request
1945 self.pull_request = pull_request
1946
1946
1947 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1947 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1948 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1948 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1949 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1949 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1950
1950
1951 user = relationship('User')
1951 user = relationship('User')
1952 pull_request = relationship('PullRequest')
1952 pull_request = relationship('PullRequest')
1953
1953
1954
1954
1955 class Notification(Base, BaseModel):
1955 class Notification(Base, BaseModel):
1956 __tablename__ = 'notifications'
1956 __tablename__ = 'notifications'
1957 __table_args__ = (
1957 __table_args__ = (
1958 Index('notification_type_idx', 'type'),
1958 Index('notification_type_idx', 'type'),
1959 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1959 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1960 'mysql_charset': 'utf8'},
1960 'mysql_charset': 'utf8'},
1961 )
1961 )
1962
1962
1963 TYPE_CHANGESET_COMMENT = u'cs_comment'
1963 TYPE_CHANGESET_COMMENT = u'cs_comment'
1964 TYPE_MESSAGE = u'message'
1964 TYPE_MESSAGE = u'message'
1965 TYPE_MENTION = u'mention'
1965 TYPE_MENTION = u'mention'
1966 TYPE_REGISTRATION = u'registration'
1966 TYPE_REGISTRATION = u'registration'
1967 TYPE_PULL_REQUEST = u'pull_request'
1967 TYPE_PULL_REQUEST = u'pull_request'
1968 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1968 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1969
1969
1970 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1970 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1971 subject = Column('subject', Unicode(512), nullable=True)
1971 subject = Column('subject', Unicode(512), nullable=True)
1972 body = Column('body', UnicodeText(50000), nullable=True)
1972 body = Column('body', UnicodeText(50000), nullable=True)
1973 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1973 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1974 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1974 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1975 type_ = Column('type', Unicode(256))
1975 type_ = Column('type', Unicode(256))
1976
1976
1977 created_by_user = relationship('User')
1977 created_by_user = relationship('User')
1978 notifications_to_users = relationship('UserNotification', lazy='joined',
1978 notifications_to_users = relationship('UserNotification', lazy='joined',
1979 cascade="all, delete, delete-orphan")
1979 cascade="all, delete, delete-orphan")
1980
1980
1981 @property
1981 @property
1982 def recipients(self):
1982 def recipients(self):
1983 return [x.user for x in UserNotification.query()\
1983 return [x.user for x in UserNotification.query()\
1984 .filter(UserNotification.notification == self)\
1984 .filter(UserNotification.notification == self)\
1985 .order_by(UserNotification.user_id.asc()).all()]
1985 .order_by(UserNotification.user_id.asc()).all()]
1986
1986
1987 @classmethod
1987 @classmethod
1988 def create(cls, created_by, subject, body, recipients, type_=None):
1988 def create(cls, created_by, subject, body, recipients, type_=None):
1989 if type_ is None:
1989 if type_ is None:
1990 type_ = Notification.TYPE_MESSAGE
1990 type_ = Notification.TYPE_MESSAGE
1991
1991
1992 notification = cls()
1992 notification = cls()
1993 notification.created_by_user = created_by
1993 notification.created_by_user = created_by
1994 notification.subject = subject
1994 notification.subject = subject
1995 notification.body = body
1995 notification.body = body
1996 notification.type_ = type_
1996 notification.type_ = type_
1997 notification.created_on = datetime.datetime.now()
1997 notification.created_on = datetime.datetime.now()
1998
1998
1999 for u in recipients:
1999 for u in recipients:
2000 assoc = UserNotification()
2000 assoc = UserNotification()
2001 assoc.notification = notification
2001 assoc.notification = notification
2002 u.notifications.append(assoc)
2002 u.notifications.append(assoc)
2003 Session().add(notification)
2003 Session().add(notification)
2004 return notification
2004 return notification
2005
2005
2006 @property
2006 @property
2007 def description(self):
2007 def description(self):
2008 from rhodecode.model.notification import NotificationModel
2008 from rhodecode.model.notification import NotificationModel
2009 return NotificationModel().make_description(self)
2009 return NotificationModel().make_description(self)
2010
2010
2011
2011
2012 class UserNotification(Base, BaseModel):
2012 class UserNotification(Base, BaseModel):
2013 __tablename__ = 'user_to_notification'
2013 __tablename__ = 'user_to_notification'
2014 __table_args__ = (
2014 __table_args__ = (
2015 UniqueConstraint('user_id', 'notification_id'),
2015 UniqueConstraint('user_id', 'notification_id'),
2016 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2016 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2017 'mysql_charset': 'utf8'}
2017 'mysql_charset': 'utf8'}
2018 )
2018 )
2019 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
2019 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
2020 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
2020 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
2021 read = Column('read', Boolean, default=False)
2021 read = Column('read', Boolean, default=False)
2022 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
2022 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
2023
2023
2024 user = relationship('User', lazy="joined")
2024 user = relationship('User', lazy="joined")
2025 notification = relationship('Notification', lazy="joined",
2025 notification = relationship('Notification', lazy="joined",
2026 order_by=lambda: Notification.created_on.desc(),)
2026 order_by=lambda: Notification.created_on.desc(),)
2027
2027
2028 def mark_as_read(self):
2028 def mark_as_read(self):
2029 self.read = True
2029 self.read = True
2030 Session().add(self)
2030 Session().add(self)
2031
2031
2032
2032
2033 class DbMigrateVersion(Base, BaseModel):
2033 class DbMigrateVersion(Base, BaseModel):
2034 __tablename__ = 'db_migrate_version'
2034 __tablename__ = 'db_migrate_version'
2035 __table_args__ = (
2035 __table_args__ = (
2036 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2036 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2037 'mysql_charset': 'utf8'},
2037 'mysql_charset': 'utf8'},
2038 )
2038 )
2039 repository_id = Column('repository_id', String(250), primary_key=True)
2039 repository_id = Column('repository_id', String(250), primary_key=True)
2040 repository_path = Column('repository_path', Text)
2040 repository_path = Column('repository_path', Text)
2041 version = Column('version', Integer)
2041 version = Column('version', Integer)
2042
@@ -1,26 +1,26 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.db_1_7_0
3 rhodecode.model.db_1_7_0
4 ~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Database Models for RhodeCode <=1.7.X
6 Database Models for RhodeCode <=1.7.X
7
7
8 :created_on: Apr 08, 2013
8 :created_on: Apr 08, 2013
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2013 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2013 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 from rhodecode.model.db import * No newline at end of file
26 from rhodecode.model.db import *
General Comments 0
You need to be logged in to leave comments. Login now