##// END OF EJS Templates
removed users_group controller in replace for model methods,...
marcink -
r1436:88d13c1c beta
parent child Browse files
Show More
@@ -1,216 +1,214 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 Users Groups crud controller for pylons
6 Users 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) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28 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.exceptions import UsersGroupsAssignedException
35 from rhodecode.lib.exceptions import UsersGroupsAssignedException
36 from rhodecode.lib import helpers as h
36 from rhodecode.lib import helpers as h
37 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
37 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
38 from rhodecode.lib.base import BaseController, render
38 from rhodecode.lib.base import BaseController, render
39
39
40 from rhodecode.model.db import User, UsersGroup, Permission, UsersGroupToPerm
40 from rhodecode.model.db import User, UsersGroup, Permission, UsersGroupToPerm
41 from rhodecode.model.forms import UserForm, UsersGroupForm
41 from rhodecode.model.forms import UserForm, UsersGroupForm
42 from rhodecode.model.users_group import UsersGroupModel
43
42
44 log = logging.getLogger(__name__)
43 log = logging.getLogger(__name__)
45
44
46
45
47 class UsersGroupsController(BaseController):
46 class UsersGroupsController(BaseController):
48 """REST Controller styled on the Atom Publishing Protocol"""
47 """REST Controller styled on the Atom Publishing Protocol"""
49 # To properly map this controller, ensure your config/routing.py
48 # To properly map this controller, ensure your config/routing.py
50 # file has a resource setup:
49 # file has a resource setup:
51 # map.resource('users_group', 'users_groups')
50 # map.resource('users_group', 'users_groups')
52
51
53 @LoginRequired()
52 @LoginRequired()
54 @HasPermissionAllDecorator('hg.admin')
53 @HasPermissionAllDecorator('hg.admin')
55 def __before__(self):
54 def __before__(self):
56 c.admin_user = session.get('admin_user')
55 c.admin_user = session.get('admin_user')
57 c.admin_username = session.get('admin_username')
56 c.admin_username = session.get('admin_username')
58 super(UsersGroupsController, self).__before__()
57 super(UsersGroupsController, self).__before__()
59 c.available_permissions = config['available_permissions']
58 c.available_permissions = config['available_permissions']
60
59
61 def index(self, format='html'):
60 def index(self, format='html'):
62 """GET /users_groups: All items in the collection"""
61 """GET /users_groups: All items in the collection"""
63 # url('users_groups')
62 # url('users_groups')
64 c.users_groups_list = self.sa.query(UsersGroup).all()
63 c.users_groups_list = self.sa.query(UsersGroup).all()
65 return render('admin/users_groups/users_groups.html')
64 return render('admin/users_groups/users_groups.html')
66
65
67 def create(self):
66 def create(self):
68 """POST /users_groups: Create a new item"""
67 """POST /users_groups: Create a new item"""
69 # url('users_groups')
68 # url('users_groups')
70 users_group_model = UsersGroupModel()
69
71 users_group_form = UsersGroupForm()()
70 users_group_form = UsersGroupForm()()
72 try:
71 try:
73 form_result = users_group_form.to_python(dict(request.POST))
72 form_result = users_group_form.to_python(dict(request.POST))
74 users_group_model.create(form_result)
73 UsersGroup.create(form_result)
75 h.flash(_('created users group %s') \
74 h.flash(_('created users group %s') \
76 % form_result['users_group_name'], category='success')
75 % form_result['users_group_name'], category='success')
77 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
76 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
78 except formencode.Invalid, errors:
77 except formencode.Invalid, errors:
79 return htmlfill.render(
78 return htmlfill.render(
80 render('admin/users_groups/users_group_add.html'),
79 render('admin/users_groups/users_group_add.html'),
81 defaults=errors.value,
80 defaults=errors.value,
82 errors=errors.error_dict or {},
81 errors=errors.error_dict or {},
83 prefix_error=False,
82 prefix_error=False,
84 encoding="UTF-8")
83 encoding="UTF-8")
85 except Exception:
84 except Exception:
86 log.error(traceback.format_exc())
85 log.error(traceback.format_exc())
87 h.flash(_('error occurred during creation of users group %s') \
86 h.flash(_('error occurred during creation of users group %s') \
88 % request.POST.get('users_group_name'), category='error')
87 % request.POST.get('users_group_name'), category='error')
89
88
90 return redirect(url('users_groups'))
89 return redirect(url('users_groups'))
91
90
92 def new(self, format='html'):
91 def new(self, format='html'):
93 """GET /users_groups/new: Form to create a new item"""
92 """GET /users_groups/new: Form to create a new item"""
94 # url('new_users_group')
93 # url('new_users_group')
95 return render('admin/users_groups/users_group_add.html')
94 return render('admin/users_groups/users_group_add.html')
96
95
97 def update(self, id):
96 def update(self, id):
98 """PUT /users_groups/id: Update an existing item"""
97 """PUT /users_groups/id: Update an existing item"""
99 # Forms posted to this method should contain a hidden field:
98 # Forms posted to this method should contain a hidden field:
100 # <input type="hidden" name="_method" value="PUT" />
99 # <input type="hidden" name="_method" value="PUT" />
101 # Or using helpers:
100 # Or using helpers:
102 # h.form(url('users_group', id=ID),
101 # h.form(url('users_group', id=ID),
103 # method='put')
102 # method='put')
104 # url('users_group', id=ID)
103 # url('users_group', id=ID)
105
104
106 users_group_model = UsersGroupModel()
105 c.users_group = UsersGroup.get(id)
107 c.users_group = users_group_model.get(id)
108 c.group_members = [(x.user_id, x.user.username) for x in
106 c.group_members = [(x.user_id, x.user.username) for x in
109 c.users_group.members]
107 c.users_group.members]
110
108
111 c.available_members = [(x.user_id, x.username) for x in
109 c.available_members = [(x.user_id, x.username) for x in
112 self.sa.query(User).all()]
110 self.sa.query(User).all()]
113 users_group_form = UsersGroupForm(edit=True,
111 users_group_form = UsersGroupForm(edit=True,
114 old_data=c.users_group.get_dict(),
112 old_data=c.users_group.get_dict(),
115 available_members=[str(x[0]) for x
113 available_members=[str(x[0]) for x
116 in c.available_members])()
114 in c.available_members])()
117
115
118 try:
116 try:
119 form_result = users_group_form.to_python(request.POST)
117 form_result = users_group_form.to_python(request.POST)
120 users_group_model.update(id, form_result)
118 UsersGroup.update(id, form_result)
121 h.flash(_('updated users group %s') \
119 h.flash(_('updated users group %s') \
122 % form_result['users_group_name'],
120 % form_result['users_group_name'],
123 category='success')
121 category='success')
124 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
122 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
125 except formencode.Invalid, errors:
123 except formencode.Invalid, errors:
126 e = errors.error_dict or {}
124 e = errors.error_dict or {}
127
125
128 perm = Permission.get_by_key('hg.create.repository')
126 perm = Permission.get_by_key('hg.create.repository')
129 e.update({'create_repo_perm':
127 e.update({'create_repo_perm':
130 UsersGroupToPerm.has_perm(id, perm)})
128 UsersGroupToPerm.has_perm(id, perm)})
131
129
132 return htmlfill.render(
130 return htmlfill.render(
133 render('admin/users_groups/users_group_edit.html'),
131 render('admin/users_groups/users_group_edit.html'),
134 defaults=errors.value,
132 defaults=errors.value,
135 errors=e,
133 errors=e,
136 prefix_error=False,
134 prefix_error=False,
137 encoding="UTF-8")
135 encoding="UTF-8")
138 except Exception:
136 except Exception:
139 log.error(traceback.format_exc())
137 log.error(traceback.format_exc())
140 h.flash(_('error occurred during update of users group %s') \
138 h.flash(_('error occurred during update of users group %s') \
141 % request.POST.get('users_group_name'), category='error')
139 % request.POST.get('users_group_name'), category='error')
142
140
143 return redirect(url('users_groups'))
141 return redirect(url('users_groups'))
144
142
145 def delete(self, id):
143 def delete(self, id):
146 """DELETE /users_groups/id: Delete an existing item"""
144 """DELETE /users_groups/id: Delete an existing item"""
147 # Forms posted to this method should contain a hidden field:
145 # Forms posted to this method should contain a hidden field:
148 # <input type="hidden" name="_method" value="DELETE" />
146 # <input type="hidden" name="_method" value="DELETE" />
149 # Or using helpers:
147 # Or using helpers:
150 # h.form(url('users_group', id=ID),
148 # h.form(url('users_group', id=ID),
151 # method='delete')
149 # method='delete')
152 # url('users_group', id=ID)
150 # url('users_group', id=ID)
153 users_group_model = UsersGroupModel()
151
154 try:
152 try:
155 users_group_model.delete(id)
153 UsersGroup.delete(id)
156 h.flash(_('successfully deleted users group'), category='success')
154 h.flash(_('successfully deleted users group'), category='success')
157 except UsersGroupsAssignedException, e:
155 except UsersGroupsAssignedException, e:
158 h.flash(e, category='error')
156 h.flash(e, category='error')
159 except Exception:
157 except Exception:
160 h.flash(_('An error occurred during deletion of users group'),
158 h.flash(_('An error occurred during deletion of users group'),
161 category='error')
159 category='error')
162 return redirect(url('users_groups'))
160 return redirect(url('users_groups'))
163
161
164 def show(self, id, format='html'):
162 def show(self, id, format='html'):
165 """GET /users_groups/id: Show a specific item"""
163 """GET /users_groups/id: Show a specific item"""
166 # url('users_group', id=ID)
164 # url('users_group', id=ID)
167
165
168 def edit(self, id, format='html'):
166 def edit(self, id, format='html'):
169 """GET /users_groups/id/edit: Form to edit an existing item"""
167 """GET /users_groups/id/edit: Form to edit an existing item"""
170 # url('edit_users_group', id=ID)
168 # url('edit_users_group', id=ID)
171
169
172 c.users_group = self.sa.query(UsersGroup).get(id)
170 c.users_group = self.sa.query(UsersGroup).get(id)
173 if not c.users_group:
171 if not c.users_group:
174 return redirect(url('users_groups'))
172 return redirect(url('users_groups'))
175
173
176 c.users_group.permissions = {}
174 c.users_group.permissions = {}
177 c.group_members = [(x.user_id, x.user.username) for x in
175 c.group_members = [(x.user_id, x.user.username) for x in
178 c.users_group.members]
176 c.users_group.members]
179 c.available_members = [(x.user_id, x.username) for x in
177 c.available_members = [(x.user_id, x.username) for x in
180 self.sa.query(User).all()]
178 self.sa.query(User).all()]
181 defaults = c.users_group.get_dict()
179 defaults = c.users_group.get_dict()
182 perm = Permission.get_by_key('hg.create.repository')
180 perm = Permission.get_by_key('hg.create.repository')
183 defaults.update({'create_repo_perm':
181 defaults.update({'create_repo_perm':
184 UsersGroupToPerm.has_perm(id, perm)})
182 UsersGroupToPerm.has_perm(id, perm)})
185 return htmlfill.render(
183 return htmlfill.render(
186 render('admin/users_groups/users_group_edit.html'),
184 render('admin/users_groups/users_group_edit.html'),
187 defaults=defaults,
185 defaults=defaults,
188 encoding="UTF-8",
186 encoding="UTF-8",
189 force_defaults=False
187 force_defaults=False
190 )
188 )
191
189
192 def update_perm(self, id):
190 def update_perm(self, id):
193 """PUT /users_perm/id: Update an existing item"""
191 """PUT /users_perm/id: Update an existing item"""
194 # url('users_group_perm', id=ID, method='put')
192 # url('users_group_perm', id=ID, method='put')
195
193
196 grant_perm = request.POST.get('create_repo_perm', False)
194 grant_perm = request.POST.get('create_repo_perm', False)
197
195
198 if grant_perm:
196 if grant_perm:
199 perm = Permission.get_by_key('hg.create.none')
197 perm = Permission.get_by_key('hg.create.none')
200 UsersGroupToPerm.revoke_perm(id, perm)
198 UsersGroupToPerm.revoke_perm(id, perm)
201
199
202 perm = Permission.get_by_key('hg.create.repository')
200 perm = Permission.get_by_key('hg.create.repository')
203 UsersGroupToPerm.grant_perm(id, perm)
201 UsersGroupToPerm.grant_perm(id, perm)
204 h.flash(_("Granted 'repository create' permission to user"),
202 h.flash(_("Granted 'repository create' permission to user"),
205 category='success')
203 category='success')
206
204
207 else:
205 else:
208 perm = Permission.get_by_key('hg.create.repository')
206 perm = Permission.get_by_key('hg.create.repository')
209 UsersGroupToPerm.revoke_perm(id, perm)
207 UsersGroupToPerm.revoke_perm(id, perm)
210
208
211 perm = Permission.get_by_key('hg.create.none')
209 perm = Permission.get_by_key('hg.create.none')
212 UsersGroupToPerm.grant_perm(id, perm)
210 UsersGroupToPerm.grant_perm(id, perm)
213 h.flash(_("Revoked 'repository create' permission to user"),
211 h.flash(_("Revoked 'repository create' permission to user"),
214 category='success')
212 category='success')
215
213
216 return redirect(url('edit_users_group', id=id))
214 return redirect(url('edit_users_group', id=id))
@@ -1,803 +1,875 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.db
3 rhodecode.model.db
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 Database Models for RhodeCode
6 Database Models for RhodeCode
7
7
8 :created_on: Apr 08, 2010
8 :created_on: Apr 08, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import logging
27 import logging
28 import datetime
28 import datetime
29 import traceback
29 import traceback
30 from datetime import date
30 from datetime import date
31
31
32 from sqlalchemy import *
32 from sqlalchemy import *
33 from sqlalchemy.exc import DatabaseError
33 from sqlalchemy.exc import DatabaseError
34 from sqlalchemy.orm import relationship, backref, joinedload, class_mapper
34 from sqlalchemy.orm import relationship, backref, joinedload, class_mapper
35 from sqlalchemy.orm.interfaces import MapperExtension
35 from sqlalchemy.orm.interfaces import MapperExtension
36
36
37 from beaker.cache import cache_region, region_invalidate
37 from beaker.cache import cache_region, region_invalidate
38
38
39 from vcs import get_backend
39 from vcs import get_backend
40 from vcs.utils.helpers import get_scm
40 from vcs.utils.helpers import get_scm
41 from vcs.exceptions import RepositoryError, VCSError
41 from vcs.exceptions import RepositoryError, VCSError
42 from vcs.utils.lazy import LazyProperty
42 from vcs.utils.lazy import LazyProperty
43 from vcs.nodes import FileNode
43 from vcs.nodes import FileNode
44
44
45 from rhodecode.lib.exceptions import UsersGroupsAssignedException
45 from rhodecode.lib import str2bool, json, safe_str
46 from rhodecode.lib import str2bool, json, safe_str
46 from rhodecode.model.meta import Base, Session
47 from rhodecode.model.meta import Base, Session
47 from rhodecode.model.caching_query import FromCache
48 from rhodecode.model.caching_query import FromCache
48
49
49 log = logging.getLogger(__name__)
50 log = logging.getLogger(__name__)
50
51
51 #==============================================================================
52 #==============================================================================
52 # BASE CLASSES
53 # BASE CLASSES
53 #==============================================================================
54 #==============================================================================
54
55
55 class ModelSerializer(json.JSONEncoder):
56 class ModelSerializer(json.JSONEncoder):
56 """
57 """
57 Simple Serializer for JSON,
58 Simple Serializer for JSON,
58
59
59 usage::
60 usage::
60
61
61 to make object customized for serialization implement a __json__
62 to make object customized for serialization implement a __json__
62 method that will return a dict for serialization into json
63 method that will return a dict for serialization into json
63
64
64 example::
65 example::
65
66
66 class Task(object):
67 class Task(object):
67
68
68 def __init__(self, name, value):
69 def __init__(self, name, value):
69 self.name = name
70 self.name = name
70 self.value = value
71 self.value = value
71
72
72 def __json__(self):
73 def __json__(self):
73 return dict(name=self.name,
74 return dict(name=self.name,
74 value=self.value)
75 value=self.value)
75
76
76 """
77 """
77
78
78 def default(self, obj):
79 def default(self, obj):
79
80
80 if hasattr(obj, '__json__'):
81 if hasattr(obj, '__json__'):
81 return obj.__json__()
82 return obj.__json__()
82 else:
83 else:
83 return json.JSONEncoder.default(self, obj)
84 return json.JSONEncoder.default(self, obj)
84
85
85 class BaseModel(object):
86 class BaseModel(object):
86 """Base Model for all classess
87 """Base Model for all classess
87
88
88 """
89 """
89
90
90 @classmethod
91 @classmethod
91 def _get_keys(cls):
92 def _get_keys(cls):
92 """return column names for this model """
93 """return column names for this model """
93 return class_mapper(cls).c.keys()
94 return class_mapper(cls).c.keys()
94
95
95 def get_dict(self):
96 def get_dict(self):
96 """return dict with keys and values corresponding
97 """return dict with keys and values corresponding
97 to this model data """
98 to this model data """
98
99
99 d = {}
100 d = {}
100 for k in self._get_keys():
101 for k in self._get_keys():
101 d[k] = getattr(self, k)
102 d[k] = getattr(self, k)
102 return d
103 return d
103
104
104 def get_appstruct(self):
105 def get_appstruct(self):
105 """return list with keys and values tupples corresponding
106 """return list with keys and values tupples corresponding
106 to this model data """
107 to this model data """
107
108
108 l = []
109 l = []
109 for k in self._get_keys():
110 for k in self._get_keys():
110 l.append((k, getattr(self, k),))
111 l.append((k, getattr(self, k),))
111 return l
112 return l
112
113
113 def populate_obj(self, populate_dict):
114 def populate_obj(self, populate_dict):
114 """populate model with data from given populate_dict"""
115 """populate model with data from given populate_dict"""
115
116
116 for k in self._get_keys():
117 for k in self._get_keys():
117 if k in populate_dict:
118 if k in populate_dict:
118 setattr(self, k, populate_dict[k])
119 setattr(self, k, populate_dict[k])
119
120
120 @classmethod
121 @classmethod
121 def query(cls):
122 def query(cls):
122 return Session.query(cls)
123 return Session.query(cls)
123
124
124 @classmethod
125 @classmethod
125 def get(cls, id_):
126 def get(cls, id_):
126 return Session.query(cls).get(id_)
127 return Session.query(cls).get(id_)
127
128
128
129
129 class RhodeCodeSettings(Base, BaseModel):
130 class RhodeCodeSettings(Base, BaseModel):
130 __tablename__ = 'rhodecode_settings'
131 __tablename__ = 'rhodecode_settings'
131 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
132 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
132 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
133 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
133 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
134 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
134 app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
135 app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
135
136
136 def __init__(self, k='', v=''):
137 def __init__(self, k='', v=''):
137 self.app_settings_name = k
138 self.app_settings_name = k
138 self.app_settings_value = v
139 self.app_settings_value = v
139
140
140 def __repr__(self):
141 def __repr__(self):
141 return "<%s('%s:%s')>" % (self.__class__.__name__,
142 return "<%s('%s:%s')>" % (self.__class__.__name__,
142 self.app_settings_name, self.app_settings_value)
143 self.app_settings_name, self.app_settings_value)
143
144
144
145
145 @classmethod
146 @classmethod
146 def get_by_name(cls, ldap_key):
147 def get_by_name(cls, ldap_key):
147 return Session.query(cls)\
148 return Session.query(cls)\
148 .filter(cls.app_settings_name == ldap_key).scalar()
149 .filter(cls.app_settings_name == ldap_key).scalar()
149
150
150 @classmethod
151 @classmethod
151 def get_app_settings(cls, cache=False):
152 def get_app_settings(cls, cache=False):
152
153
153 ret = Session.query(cls)
154 ret = Session.query(cls)
154
155
155 if cache:
156 if cache:
156 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
157 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
157
158
158 if not ret:
159 if not ret:
159 raise Exception('Could not get application settings !')
160 raise Exception('Could not get application settings !')
160 settings = {}
161 settings = {}
161 for each in ret:
162 for each in ret:
162 settings['rhodecode_' + each.app_settings_name] = \
163 settings['rhodecode_' + each.app_settings_name] = \
163 each.app_settings_value
164 each.app_settings_value
164
165
165 return settings
166 return settings
166
167
167 @classmethod
168 @classmethod
168 def get_ldap_settings(cls, cache=False):
169 def get_ldap_settings(cls, cache=False):
169 ret = Session.query(cls)\
170 ret = Session.query(cls)\
170 .filter(cls.app_settings_name.startswith('ldap_'))\
171 .filter(cls.app_settings_name.startswith('ldap_'))\
171 .all()
172 .all()
172 fd = {}
173 fd = {}
173 for row in ret:
174 for row in ret:
174 fd.update({row.app_settings_name:row.app_settings_value})
175 fd.update({row.app_settings_name:row.app_settings_value})
175
176
176 fd.update({'ldap_active':str2bool(fd.get('ldap_active'))})
177 fd.update({'ldap_active':str2bool(fd.get('ldap_active'))})
177
178
178 return fd
179 return fd
179
180
180
181
181 class RhodeCodeUi(Base, BaseModel):
182 class RhodeCodeUi(Base, BaseModel):
182 __tablename__ = 'rhodecode_ui'
183 __tablename__ = 'rhodecode_ui'
183 __table_args__ = {'extend_existing':True}
184 __table_args__ = {'extend_existing':True}
184 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
185 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
185 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
186 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
186 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
187 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
187 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
188 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
188 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
189 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
189
190
190
191
191 @classmethod
192 @classmethod
192 def get_by_key(cls, key):
193 def get_by_key(cls, key):
193 return Session.query(cls).filter(cls.ui_key == key)
194 return Session.query(cls).filter(cls.ui_key == key)
194
195
195
196
196 class User(Base, BaseModel):
197 class User(Base, BaseModel):
197 __tablename__ = 'users'
198 __tablename__ = 'users'
198 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
199 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
199 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
200 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
200 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
201 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
201 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
202 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
202 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
203 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
203 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
204 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
204 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
205 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
205 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
206 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
206 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
207 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
207 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
208 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
208 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
209 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
209 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
210 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
210
211
211 user_log = relationship('UserLog', cascade='all')
212 user_log = relationship('UserLog', cascade='all')
212 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
213 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
213
214
214 repositories = relationship('Repository')
215 repositories = relationship('Repository')
215 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
216 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
216 repo_to_perm = relationship('RepoToPerm', primaryjoin='RepoToPerm.user_id==User.user_id', cascade='all')
217 repo_to_perm = relationship('RepoToPerm', primaryjoin='RepoToPerm.user_id==User.user_id', cascade='all')
217
218
218 group_member = relationship('UsersGroupMember', cascade='all')
219 group_member = relationship('UsersGroupMember', cascade='all')
219
220
220 @property
221 @property
221 def full_contact(self):
222 def full_contact(self):
222 return '%s %s <%s>' % (self.name, self.lastname, self.email)
223 return '%s %s <%s>' % (self.name, self.lastname, self.email)
223
224
224 @property
225 @property
225 def short_contact(self):
226 def short_contact(self):
226 return '%s %s' % (self.name, self.lastname)
227 return '%s %s' % (self.name, self.lastname)
227
228
228 @property
229 @property
229 def is_admin(self):
230 def is_admin(self):
230 return self.admin
231 return self.admin
231
232
232 def __repr__(self):
233 def __repr__(self):
233 try:
234 try:
234 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
235 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
235 self.user_id, self.username)
236 self.user_id, self.username)
236 except:
237 except:
237 return self.__class__.__name__
238 return self.__class__.__name__
238
239
239 @classmethod
240 @classmethod
240 def by_username(cls, username, case_insensitive=False):
241 def by_username(cls, username, case_insensitive=False):
241 if case_insensitive:
242 if case_insensitive:
242 return Session.query(cls).filter(cls.username.like(username)).one()
243 return Session.query(cls).filter(cls.username.like(username)).one()
243 else:
244 else:
244 return Session.query(cls).filter(cls.username == username).one()
245 return Session.query(cls).filter(cls.username == username).one()
245
246
246 @classmethod
247 @classmethod
247 def get_by_api_key(cls, api_key):
248 def get_by_api_key(cls, api_key):
248 return Session.query(cls).filter(cls.api_key == api_key).one()
249 return Session.query(cls).filter(cls.api_key == api_key).one()
249
250
250
251
251 def update_lastlogin(self):
252 def update_lastlogin(self):
252 """Update user lastlogin"""
253 """Update user lastlogin"""
253
254
254 self.last_login = datetime.datetime.now()
255 self.last_login = datetime.datetime.now()
255 Session.add(self)
256 Session.add(self)
256 Session.commit()
257 Session.commit()
257 log.debug('updated user %s lastlogin', self.username)
258 log.debug('updated user %s lastlogin', self.username)
258
259
259
260
260 class UserLog(Base, BaseModel):
261 class UserLog(Base, BaseModel):
261 __tablename__ = 'user_logs'
262 __tablename__ = 'user_logs'
262 __table_args__ = {'extend_existing':True}
263 __table_args__ = {'extend_existing':True}
263 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
264 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
264 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
265 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
265 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
266 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
266 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
267 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
267 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
268 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
268 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
269 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
269 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
270 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
270
271
271 @property
272 @property
272 def action_as_day(self):
273 def action_as_day(self):
273 return date(*self.action_date.timetuple()[:3])
274 return date(*self.action_date.timetuple()[:3])
274
275
275 user = relationship('User')
276 user = relationship('User')
276 repository = relationship('Repository')
277 repository = relationship('Repository')
277
278
278
279
279 class UsersGroup(Base, BaseModel):
280 class UsersGroup(Base, BaseModel):
280 __tablename__ = 'users_groups'
281 __tablename__ = 'users_groups'
281 __table_args__ = {'extend_existing':True}
282 __table_args__ = {'extend_existing':True}
282
283
283 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
284 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
284 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
285 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
285 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
286 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
286
287
287 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
288 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
288
289
289 def __repr__(self):
290 def __repr__(self):
290 return '<userGroup(%s)>' % (self.users_group_name)
291 return '<userGroup(%s)>' % (self.users_group_name)
291
292
292 @classmethod
293 @classmethod
293 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
294 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
294 if case_insensitive:
295 if case_insensitive:
295 gr = Session.query(cls)\
296 gr = Session.query(cls)\
296 .filter(cls.users_group_name.ilike(group_name))
297 .filter(cls.users_group_name.ilike(group_name))
297 else:
298 else:
298 gr = Session.query(UsersGroup)\
299 gr = Session.query(UsersGroup)\
299 .filter(UsersGroup.users_group_name == group_name)
300 .filter(UsersGroup.users_group_name == group_name)
300 if cache:
301 if cache:
301 gr = gr.options(FromCache("sql_cache_short",
302 gr = gr.options(FromCache("sql_cache_short",
302 "get_user_%s" % group_name))
303 "get_user_%s" % group_name))
303 return gr.scalar()
304 return gr.scalar()
304
305
306
307 @classmethod
308 def get(cls, users_group_id, cache=False):
309 users_group = Session.query(cls)
310 if cache:
311 users_group = users_group.options(FromCache("sql_cache_short",
312 "get_users_group_%s" % users_group_id))
313 return users_group.get(users_group_id)
314
315 @classmethod
316 def create(cls, form_data):
317 try:
318 new_users_group = cls()
319 for k, v in form_data.items():
320 setattr(new_users_group, k, v)
321
322 Session.add(new_users_group)
323 Session.commit()
324 except:
325 log.error(traceback.format_exc())
326 Session.rollback()
327 raise
328
329 @classmethod
330 def update(cls, users_group_id, form_data):
331
332 try:
333 users_group = cls.get(users_group_id, cache=False)
334
335 for k, v in form_data.items():
336 if k == 'users_group_members':
337 users_group.members = []
338 Session.flush()
339 members_list = []
340 if v:
341 for u_id in set(v):
342 members_list.append(UsersGroupMember(
343 users_group_id,
344 u_id))
345 setattr(users_group, 'members', members_list)
346 setattr(users_group, k, v)
347
348 Session.add(users_group)
349 Session.commit()
350 except:
351 log.error(traceback.format_exc())
352 Session.rollback()
353 raise
354
355 @classmethod
356 def delete(cls, users_group_id):
357 try:
358
359 # check if this group is not assigned to repo
360 assigned_groups = UsersGroupRepoToPerm.query()\
361 .filter(UsersGroupRepoToPerm.users_group_id ==
362 users_group_id).all()
363
364 if assigned_groups:
365 raise UsersGroupsAssignedException('Group assigned to %s' %
366 assigned_groups)
367
368 users_group = cls.get(users_group_id, cache=False)
369 Session.delete(users_group)
370 Session.commit()
371 except:
372 log.error(traceback.format_exc())
373 Session.rollback()
374 raise
375
376
305 class UsersGroupMember(Base, BaseModel):
377 class UsersGroupMember(Base, BaseModel):
306 __tablename__ = 'users_groups_members'
378 __tablename__ = 'users_groups_members'
307 __table_args__ = {'extend_existing':True}
379 __table_args__ = {'extend_existing':True}
308
380
309 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
381 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
310 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
382 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
311 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
383 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
312
384
313 user = relationship('User', lazy='joined')
385 user = relationship('User', lazy='joined')
314 users_group = relationship('UsersGroup')
386 users_group = relationship('UsersGroup')
315
387
316 def __init__(self, gr_id='', u_id=''):
388 def __init__(self, gr_id='', u_id=''):
317 self.users_group_id = gr_id
389 self.users_group_id = gr_id
318 self.user_id = u_id
390 self.user_id = u_id
319
391
320 class Repository(Base, BaseModel):
392 class Repository(Base, BaseModel):
321 __tablename__ = 'repositories'
393 __tablename__ = 'repositories'
322 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
394 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
323
395
324 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
396 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
325 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
397 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
326 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
398 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
327 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
399 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
328 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
400 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
329 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
401 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
330 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
402 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
331 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
403 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
332 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
404 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
333 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
405 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
334
406
335 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
407 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
336 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
408 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
337
409
338
410
339 user = relationship('User')
411 user = relationship('User')
340 fork = relationship('Repository', remote_side=repo_id)
412 fork = relationship('Repository', remote_side=repo_id)
341 group = relationship('Group')
413 group = relationship('Group')
342 repo_to_perm = relationship('RepoToPerm', cascade='all', order_by='RepoToPerm.repo_to_perm_id')
414 repo_to_perm = relationship('RepoToPerm', cascade='all', order_by='RepoToPerm.repo_to_perm_id')
343 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
415 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
344 stats = relationship('Statistics', cascade='all', uselist=False)
416 stats = relationship('Statistics', cascade='all', uselist=False)
345
417
346 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
418 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
347
419
348 logs = relationship('UserLog', cascade='all')
420 logs = relationship('UserLog', cascade='all')
349
421
350 def __repr__(self):
422 def __repr__(self):
351 return "<%s('%s:%s')>" % (self.__class__.__name__,
423 return "<%s('%s:%s')>" % (self.__class__.__name__,
352 self.repo_id, self.repo_name)
424 self.repo_id, self.repo_name)
353
425
354 @classmethod
426 @classmethod
355 def by_repo_name(cls, repo_name):
427 def by_repo_name(cls, repo_name):
356 q = Session.query(cls).filter(cls.repo_name == repo_name)
428 q = Session.query(cls).filter(cls.repo_name == repo_name)
357
429
358 q = q.options(joinedload(Repository.fork))\
430 q = q.options(joinedload(Repository.fork))\
359 .options(joinedload(Repository.user))\
431 .options(joinedload(Repository.user))\
360 .options(joinedload(Repository.group))\
432 .options(joinedload(Repository.group))\
361
433
362 return q.one()
434 return q.one()
363
435
364 @classmethod
436 @classmethod
365 def get_repo_forks(cls, repo_id):
437 def get_repo_forks(cls, repo_id):
366 return Session.query(cls).filter(Repository.fork_id == repo_id)
438 return Session.query(cls).filter(Repository.fork_id == repo_id)
367
439
368 @property
440 @property
369 def just_name(self):
441 def just_name(self):
370 return self.repo_name.split(os.sep)[-1]
442 return self.repo_name.split(os.sep)[-1]
371
443
372 @property
444 @property
373 def groups_with_parents(self):
445 def groups_with_parents(self):
374 groups = []
446 groups = []
375 if self.group is None:
447 if self.group is None:
376 return groups
448 return groups
377
449
378 cur_gr = self.group
450 cur_gr = self.group
379 groups.insert(0, cur_gr)
451 groups.insert(0, cur_gr)
380 while 1:
452 while 1:
381 gr = getattr(cur_gr, 'parent_group', None)
453 gr = getattr(cur_gr, 'parent_group', None)
382 cur_gr = cur_gr.parent_group
454 cur_gr = cur_gr.parent_group
383 if gr is None:
455 if gr is None:
384 break
456 break
385 groups.insert(0, gr)
457 groups.insert(0, gr)
386
458
387 return groups
459 return groups
388
460
389 @property
461 @property
390 def groups_and_repo(self):
462 def groups_and_repo(self):
391 return self.groups_with_parents, self.just_name
463 return self.groups_with_parents, self.just_name
392
464
393 @LazyProperty
465 @LazyProperty
394 def repo_path(self):
466 def repo_path(self):
395 """
467 """
396 Returns base full path for that repository means where it actually
468 Returns base full path for that repository means where it actually
397 exists on a filesystem
469 exists on a filesystem
398 """
470 """
399 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/')
471 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/')
400 q.options(FromCache("sql_cache_short", "repository_repo_path"))
472 q.options(FromCache("sql_cache_short", "repository_repo_path"))
401 return q.one().ui_value
473 return q.one().ui_value
402
474
403 @property
475 @property
404 def repo_full_path(self):
476 def repo_full_path(self):
405 p = [self.repo_path]
477 p = [self.repo_path]
406 # we need to split the name by / since this is how we store the
478 # we need to split the name by / since this is how we store the
407 # names in the database, but that eventually needs to be converted
479 # names in the database, but that eventually needs to be converted
408 # into a valid system path
480 # into a valid system path
409 p += self.repo_name.split('/')
481 p += self.repo_name.split('/')
410 return os.path.join(*p)
482 return os.path.join(*p)
411
483
412 @property
484 @property
413 def _ui(self):
485 def _ui(self):
414 """
486 """
415 Creates an db based ui object for this repository
487 Creates an db based ui object for this repository
416 """
488 """
417 from mercurial import ui
489 from mercurial import ui
418 from mercurial import config
490 from mercurial import config
419 baseui = ui.ui()
491 baseui = ui.ui()
420
492
421 #clean the baseui object
493 #clean the baseui object
422 baseui._ocfg = config.config()
494 baseui._ocfg = config.config()
423 baseui._ucfg = config.config()
495 baseui._ucfg = config.config()
424 baseui._tcfg = config.config()
496 baseui._tcfg = config.config()
425
497
426
498
427 ret = Session.query(RhodeCodeUi)\
499 ret = Session.query(RhodeCodeUi)\
428 .options(FromCache("sql_cache_short",
500 .options(FromCache("sql_cache_short",
429 "repository_repo_ui")).all()
501 "repository_repo_ui")).all()
430
502
431 hg_ui = ret
503 hg_ui = ret
432 for ui_ in hg_ui:
504 for ui_ in hg_ui:
433 if ui_.ui_active:
505 if ui_.ui_active:
434 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
506 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
435 ui_.ui_key, ui_.ui_value)
507 ui_.ui_key, ui_.ui_value)
436 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
508 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
437
509
438 return baseui
510 return baseui
439
511
440 #==========================================================================
512 #==========================================================================
441 # SCM CACHE INSTANCE
513 # SCM CACHE INSTANCE
442 #==========================================================================
514 #==========================================================================
443
515
444 @property
516 @property
445 def invalidate(self):
517 def invalidate(self):
446 """
518 """
447 Returns Invalidation object if this repo should be invalidated
519 Returns Invalidation object if this repo should be invalidated
448 None otherwise. `cache_active = False` means that this cache
520 None otherwise. `cache_active = False` means that this cache
449 state is not valid and needs to be invalidated
521 state is not valid and needs to be invalidated
450 """
522 """
451 return Session.query(CacheInvalidation)\
523 return Session.query(CacheInvalidation)\
452 .filter(CacheInvalidation.cache_key == self.repo_name)\
524 .filter(CacheInvalidation.cache_key == self.repo_name)\
453 .filter(CacheInvalidation.cache_active == False)\
525 .filter(CacheInvalidation.cache_active == False)\
454 .scalar()
526 .scalar()
455
527
456 def set_invalidate(self):
528 def set_invalidate(self):
457 """
529 """
458 set a cache for invalidation for this instance
530 set a cache for invalidation for this instance
459 """
531 """
460 inv = Session.query(CacheInvalidation)\
532 inv = Session.query(CacheInvalidation)\
461 .filter(CacheInvalidation.cache_key == self.repo_name)\
533 .filter(CacheInvalidation.cache_key == self.repo_name)\
462 .scalar()
534 .scalar()
463
535
464 if inv is None:
536 if inv is None:
465 inv = CacheInvalidation(self.repo_name)
537 inv = CacheInvalidation(self.repo_name)
466 inv.cache_active = True
538 inv.cache_active = True
467 Session.add(inv)
539 Session.add(inv)
468 Session.commit()
540 Session.commit()
469
541
470 @property
542 @property
471 def scm_instance(self):
543 def scm_instance(self):
472 return self.__get_instance()
544 return self.__get_instance()
473
545
474 @property
546 @property
475 def scm_instance_cached(self):
547 def scm_instance_cached(self):
476 @cache_region('long_term')
548 @cache_region('long_term')
477 def _c(repo_name):
549 def _c(repo_name):
478 return self.__get_instance()
550 return self.__get_instance()
479
551
480 # TODO: remove this trick when beaker 1.6 is released
552 # TODO: remove this trick when beaker 1.6 is released
481 # and have fixed this issue with not supporting unicode keys
553 # and have fixed this issue with not supporting unicode keys
482 rn = safe_str(self.repo_name)
554 rn = safe_str(self.repo_name)
483
555
484 inv = self.invalidate
556 inv = self.invalidate
485 if inv is not None:
557 if inv is not None:
486 region_invalidate(_c, None, rn)
558 region_invalidate(_c, None, rn)
487 # update our cache
559 # update our cache
488 inv.cache_active = True
560 inv.cache_active = True
489 Session.add(inv)
561 Session.add(inv)
490 Session.commit()
562 Session.commit()
491
563
492 return _c(rn)
564 return _c(rn)
493
565
494 def __get_instance(self):
566 def __get_instance(self):
495
567
496 repo_full_path = self.repo_full_path
568 repo_full_path = self.repo_full_path
497
569
498 try:
570 try:
499 alias = get_scm(repo_full_path)[0]
571 alias = get_scm(repo_full_path)[0]
500 log.debug('Creating instance of %s repository', alias)
572 log.debug('Creating instance of %s repository', alias)
501 backend = get_backend(alias)
573 backend = get_backend(alias)
502 except VCSError:
574 except VCSError:
503 log.error(traceback.format_exc())
575 log.error(traceback.format_exc())
504 log.error('Perhaps this repository is in db and not in '
576 log.error('Perhaps this repository is in db and not in '
505 'filesystem run rescan repositories with '
577 'filesystem run rescan repositories with '
506 '"destroy old data " option from admin panel')
578 '"destroy old data " option from admin panel')
507 return
579 return
508
580
509 if alias == 'hg':
581 if alias == 'hg':
510
582
511 repo = backend(safe_str(repo_full_path), create=False,
583 repo = backend(safe_str(repo_full_path), create=False,
512 baseui=self._ui)
584 baseui=self._ui)
513 #skip hidden web repository
585 #skip hidden web repository
514 if repo._get_hidden():
586 if repo._get_hidden():
515 return
587 return
516 else:
588 else:
517 repo = backend(repo_full_path, create=False)
589 repo = backend(repo_full_path, create=False)
518
590
519 return repo
591 return repo
520
592
521
593
522 class Group(Base, BaseModel):
594 class Group(Base, BaseModel):
523 __tablename__ = 'groups'
595 __tablename__ = 'groups'
524 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
596 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
525 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
597 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
526 __mapper_args__ = {'order_by':'group_name'}
598 __mapper_args__ = {'order_by':'group_name'}
527
599
528 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
600 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
529 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
601 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
530 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
602 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
531 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
603 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
532
604
533 parent_group = relationship('Group', remote_side=group_id)
605 parent_group = relationship('Group', remote_side=group_id)
534
606
535
607
536 def __init__(self, group_name='', parent_group=None):
608 def __init__(self, group_name='', parent_group=None):
537 self.group_name = group_name
609 self.group_name = group_name
538 self.parent_group = parent_group
610 self.parent_group = parent_group
539
611
540 def __repr__(self):
612 def __repr__(self):
541 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
613 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
542 self.group_name)
614 self.group_name)
543
615
544 @classmethod
616 @classmethod
545 def url_sep(cls):
617 def url_sep(cls):
546 return '/'
618 return '/'
547
619
548 @property
620 @property
549 def parents(self):
621 def parents(self):
550 parents_recursion_limit = 5
622 parents_recursion_limit = 5
551 groups = []
623 groups = []
552 if self.parent_group is None:
624 if self.parent_group is None:
553 return groups
625 return groups
554 cur_gr = self.parent_group
626 cur_gr = self.parent_group
555 groups.insert(0, cur_gr)
627 groups.insert(0, cur_gr)
556 cnt = 0
628 cnt = 0
557 while 1:
629 while 1:
558 cnt += 1
630 cnt += 1
559 gr = getattr(cur_gr, 'parent_group', None)
631 gr = getattr(cur_gr, 'parent_group', None)
560 cur_gr = cur_gr.parent_group
632 cur_gr = cur_gr.parent_group
561 if gr is None:
633 if gr is None:
562 break
634 break
563 if cnt == parents_recursion_limit:
635 if cnt == parents_recursion_limit:
564 # this will prevent accidental infinit loops
636 # this will prevent accidental infinit loops
565 log.error('group nested more than %s' %
637 log.error('group nested more than %s' %
566 parents_recursion_limit)
638 parents_recursion_limit)
567 break
639 break
568
640
569 groups.insert(0, gr)
641 groups.insert(0, gr)
570 return groups
642 return groups
571
643
572 @property
644 @property
573 def children(self):
645 def children(self):
574 return Session.query(Group).filter(Group.parent_group == self)
646 return Session.query(Group).filter(Group.parent_group == self)
575
647
576 @property
648 @property
577 def full_path(self):
649 def full_path(self):
578 return Group.url_sep().join([g.group_name for g in self.parents] +
650 return Group.url_sep().join([g.group_name for g in self.parents] +
579 [self.group_name])
651 [self.group_name])
580
652
581 @property
653 @property
582 def repositories(self):
654 def repositories(self):
583 return Session.query(Repository).filter(Repository.group == self)
655 return Session.query(Repository).filter(Repository.group == self)
584
656
585 @property
657 @property
586 def repositories_recursive_count(self):
658 def repositories_recursive_count(self):
587 cnt = self.repositories.count()
659 cnt = self.repositories.count()
588
660
589 def children_count(group):
661 def children_count(group):
590 cnt = 0
662 cnt = 0
591 for child in group.children:
663 for child in group.children:
592 cnt += child.repositories.count()
664 cnt += child.repositories.count()
593 cnt += children_count(child)
665 cnt += children_count(child)
594 return cnt
666 return cnt
595
667
596 return cnt + children_count(self)
668 return cnt + children_count(self)
597
669
598 class Permission(Base, BaseModel):
670 class Permission(Base, BaseModel):
599 __tablename__ = 'permissions'
671 __tablename__ = 'permissions'
600 __table_args__ = {'extend_existing':True}
672 __table_args__ = {'extend_existing':True}
601 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
673 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
602 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
674 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
603 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
675 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
604
676
605 def __repr__(self):
677 def __repr__(self):
606 return "<%s('%s:%s')>" % (self.__class__.__name__,
678 return "<%s('%s:%s')>" % (self.__class__.__name__,
607 self.permission_id, self.permission_name)
679 self.permission_id, self.permission_name)
608
680
609 @classmethod
681 @classmethod
610 def get_by_key(cls, key):
682 def get_by_key(cls, key):
611 return Session.query(cls).filter(cls.permission_name == key).scalar()
683 return Session.query(cls).filter(cls.permission_name == key).scalar()
612
684
613 class RepoToPerm(Base, BaseModel):
685 class RepoToPerm(Base, BaseModel):
614 __tablename__ = 'repo_to_perm'
686 __tablename__ = 'repo_to_perm'
615 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True})
687 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True})
616 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
688 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
617 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
689 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
618 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
690 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
619 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)
620
692
621 user = relationship('User')
693 user = relationship('User')
622 permission = relationship('Permission')
694 permission = relationship('Permission')
623 repository = relationship('Repository')
695 repository = relationship('Repository')
624
696
625 class UserToPerm(Base, BaseModel):
697 class UserToPerm(Base, BaseModel):
626 __tablename__ = 'user_to_perm'
698 __tablename__ = 'user_to_perm'
627 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
699 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
628 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
700 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
629 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
701 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
630 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
702 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
631
703
632 user = relationship('User')
704 user = relationship('User')
633 permission = relationship('Permission')
705 permission = relationship('Permission')
634
706
635 @classmethod
707 @classmethod
636 def has_perm(cls, user_id, perm):
708 def has_perm(cls, user_id, perm):
637 if not isinstance(perm, Permission):
709 if not isinstance(perm, Permission):
638 raise Exception('perm needs to be an instance of Permission class')
710 raise Exception('perm needs to be an instance of Permission class')
639
711
640 return Session.query(cls).filter(cls.user_id == user_id)\
712 return Session.query(cls).filter(cls.user_id == user_id)\
641 .filter(cls.permission == perm).scalar() is not None
713 .filter(cls.permission == perm).scalar() is not None
642
714
643 @classmethod
715 @classmethod
644 def grant_perm(cls, user_id, perm):
716 def grant_perm(cls, user_id, perm):
645 if not isinstance(perm, Permission):
717 if not isinstance(perm, Permission):
646 raise Exception('perm needs to be an instance of Permission class')
718 raise Exception('perm needs to be an instance of Permission class')
647
719
648 new = cls()
720 new = cls()
649 new.user_id = user_id
721 new.user_id = user_id
650 new.permission = perm
722 new.permission = perm
651 try:
723 try:
652 Session.add(new)
724 Session.add(new)
653 Session.commit()
725 Session.commit()
654 except:
726 except:
655 Session.rollback()
727 Session.rollback()
656
728
657
729
658 @classmethod
730 @classmethod
659 def revoke_perm(cls, user_id, perm):
731 def revoke_perm(cls, user_id, perm):
660 if not isinstance(perm, Permission):
732 if not isinstance(perm, Permission):
661 raise Exception('perm needs to be an instance of Permission class')
733 raise Exception('perm needs to be an instance of Permission class')
662
734
663 try:
735 try:
664 Session.query(cls).filter(cls.user_id == user_id)\
736 Session.query(cls).filter(cls.user_id == user_id)\
665 .filter(cls.permission == perm).delete()
737 .filter(cls.permission == perm).delete()
666 Session.commit()
738 Session.commit()
667 except:
739 except:
668 Session.rollback()
740 Session.rollback()
669
741
670 class UsersGroupRepoToPerm(Base, BaseModel):
742 class UsersGroupRepoToPerm(Base, BaseModel):
671 __tablename__ = 'users_group_repo_to_perm'
743 __tablename__ = 'users_group_repo_to_perm'
672 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True})
744 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True})
673 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
745 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
674 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
746 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
675 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
747 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
676 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
748 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
677
749
678 users_group = relationship('UsersGroup')
750 users_group = relationship('UsersGroup')
679 permission = relationship('Permission')
751 permission = relationship('Permission')
680 repository = relationship('Repository')
752 repository = relationship('Repository')
681
753
682 def __repr__(self):
754 def __repr__(self):
683 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
755 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
684
756
685 class UsersGroupToPerm(Base, BaseModel):
757 class UsersGroupToPerm(Base, BaseModel):
686 __tablename__ = 'users_group_to_perm'
758 __tablename__ = 'users_group_to_perm'
687 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
759 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
688 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
760 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
689 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
761 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
690
762
691 users_group = relationship('UsersGroup')
763 users_group = relationship('UsersGroup')
692 permission = relationship('Permission')
764 permission = relationship('Permission')
693
765
694
766
695 @classmethod
767 @classmethod
696 def has_perm(cls, users_group_id, perm):
768 def has_perm(cls, users_group_id, perm):
697 if not isinstance(perm, Permission):
769 if not isinstance(perm, Permission):
698 raise Exception('perm needs to be an instance of Permission class')
770 raise Exception('perm needs to be an instance of Permission class')
699
771
700 return Session.query(cls).filter(cls.users_group_id ==
772 return Session.query(cls).filter(cls.users_group_id ==
701 users_group_id)\
773 users_group_id)\
702 .filter(cls.permission == perm)\
774 .filter(cls.permission == perm)\
703 .scalar() is not None
775 .scalar() is not None
704
776
705 @classmethod
777 @classmethod
706 def grant_perm(cls, users_group_id, perm):
778 def grant_perm(cls, users_group_id, perm):
707 if not isinstance(perm, Permission):
779 if not isinstance(perm, Permission):
708 raise Exception('perm needs to be an instance of Permission class')
780 raise Exception('perm needs to be an instance of Permission class')
709
781
710 new = cls()
782 new = cls()
711 new.users_group_id = users_group_id
783 new.users_group_id = users_group_id
712 new.permission = perm
784 new.permission = perm
713 try:
785 try:
714 Session.add(new)
786 Session.add(new)
715 Session.commit()
787 Session.commit()
716 except:
788 except:
717 Session.rollback()
789 Session.rollback()
718
790
719
791
720 @classmethod
792 @classmethod
721 def revoke_perm(cls, users_group_id, perm):
793 def revoke_perm(cls, users_group_id, perm):
722 if not isinstance(perm, Permission):
794 if not isinstance(perm, Permission):
723 raise Exception('perm needs to be an instance of Permission class')
795 raise Exception('perm needs to be an instance of Permission class')
724
796
725 try:
797 try:
726 Session.query(cls).filter(cls.users_group_id == users_group_id)\
798 Session.query(cls).filter(cls.users_group_id == users_group_id)\
727 .filter(cls.permission == perm).delete()
799 .filter(cls.permission == perm).delete()
728 Session.commit()
800 Session.commit()
729 except:
801 except:
730 Session.rollback()
802 Session.rollback()
731
803
732
804
733 class GroupToPerm(Base, BaseModel):
805 class GroupToPerm(Base, BaseModel):
734 __tablename__ = 'group_to_perm'
806 __tablename__ = 'group_to_perm'
735 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
807 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
736
808
737 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
809 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
738 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
810 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
739 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
811 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
740 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
812 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
741
813
742 user = relationship('User')
814 user = relationship('User')
743 permission = relationship('Permission')
815 permission = relationship('Permission')
744 group = relationship('Group')
816 group = relationship('Group')
745
817
746 class Statistics(Base, BaseModel):
818 class Statistics(Base, BaseModel):
747 __tablename__ = 'statistics'
819 __tablename__ = 'statistics'
748 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True})
820 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True})
749 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
821 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
750 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
822 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
751 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
823 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
752 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
824 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
753 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
825 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
754 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
826 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
755
827
756 repository = relationship('Repository', single_parent=True)
828 repository = relationship('Repository', single_parent=True)
757
829
758 class UserFollowing(Base, BaseModel):
830 class UserFollowing(Base, BaseModel):
759 __tablename__ = 'user_followings'
831 __tablename__ = 'user_followings'
760 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
832 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
761 UniqueConstraint('user_id', 'follows_user_id')
833 UniqueConstraint('user_id', 'follows_user_id')
762 , {'extend_existing':True})
834 , {'extend_existing':True})
763
835
764 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
836 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
765 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
837 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
766 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
838 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
767 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
839 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
768 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
840 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
769
841
770 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
842 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
771
843
772 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
844 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
773 follows_repository = relationship('Repository', order_by='Repository.repo_name')
845 follows_repository = relationship('Repository', order_by='Repository.repo_name')
774
846
775
847
776 @classmethod
848 @classmethod
777 def get_repo_followers(cls, repo_id):
849 def get_repo_followers(cls, repo_id):
778 return Session.query(cls).filter(cls.follows_repo_id == repo_id)
850 return Session.query(cls).filter(cls.follows_repo_id == repo_id)
779
851
780 class CacheInvalidation(Base, BaseModel):
852 class CacheInvalidation(Base, BaseModel):
781 __tablename__ = 'cache_invalidation'
853 __tablename__ = 'cache_invalidation'
782 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True})
854 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True})
783 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
855 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
784 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
856 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
785 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
857 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
786 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
858 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
787
859
788
860
789 def __init__(self, cache_key, cache_args=''):
861 def __init__(self, cache_key, cache_args=''):
790 self.cache_key = cache_key
862 self.cache_key = cache_key
791 self.cache_args = cache_args
863 self.cache_args = cache_args
792 self.cache_active = False
864 self.cache_active = False
793
865
794 def __repr__(self):
866 def __repr__(self):
795 return "<%s('%s:%s')>" % (self.__class__.__name__,
867 return "<%s('%s:%s')>" % (self.__class__.__name__,
796 self.cache_id, self.cache_key)
868 self.cache_id, self.cache_key)
797
869
798 class DbMigrateVersion(Base, BaseModel):
870 class DbMigrateVersion(Base, BaseModel):
799 __tablename__ = 'db_migrate_version'
871 __tablename__ = 'db_migrate_version'
800 __table_args__ = {'extend_existing':True}
872 __table_args__ = {'extend_existing':True}
801 repository_id = Column('repository_id', String(250), primary_key=True)
873 repository_id = Column('repository_id', String(250), primary_key=True)
802 repository_path = Column('repository_path', Text)
874 repository_path = Column('repository_path', Text)
803 version = Column('version', Integer)
875 version = Column('version', Integer)
@@ -1,179 +1,176 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2
2
3 <%inherit file="/base/base.html"/>
3 <%inherit file="/base/base.html"/>
4
4
5 <%def name="title()">
5 <%def name="title()">
6 ${c.repo_name} ${_('Changelog')} - ${c.rhodecode_name}
6 ${c.repo_name} ${_('Changelog')} - ${c.rhodecode_name}
7 </%def>
7 </%def>
8
8
9 <%def name="breadcrumbs_links()">
9 <%def name="breadcrumbs_links()">
10 ${h.link_to(u'Home',h.url('/'))}
10 ${h.link_to(u'Home',h.url('/'))}
11 &raquo;
11 &raquo;
12 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
12 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
13 &raquo;
13 &raquo;
14 ${_('Changelog')} - ${_('showing ')} ${c.size if c.size <= c.total_cs else c.total_cs} ${_('out of')} ${c.total_cs} ${_('revisions')}
14 ${_('Changelog')} - ${_('showing ')} ${c.size if c.size <= c.total_cs else c.total_cs} ${_('out of')} ${c.total_cs} ${_('revisions')}
15 </%def>
15 </%def>
16
16
17 <%def name="page_nav()">
17 <%def name="page_nav()">
18 ${self.menu('changelog')}
18 ${self.menu('changelog')}
19 </%def>
19 </%def>
20
20
21 <%def name="main()">
21 <%def name="main()">
22 <div class="box">
22 <div class="box">
23 <!-- box / title -->
23 <!-- box / title -->
24 <div class="title">
24 <div class="title">
25 ${self.breadcrumbs()}
25 ${self.breadcrumbs()}
26 </div>
26 </div>
27 <div class="table">
27 <div class="table">
28 % if c.pagination:
28 % if c.pagination:
29 <div id="graph">
29 <div id="graph">
30 <div id="graph_nodes">
30 <div id="graph_nodes">
31 <canvas id="graph_canvas"></canvas>
31 <canvas id="graph_canvas"></canvas>
32 </div>
32 </div>
33 <div id="graph_content">
33 <div id="graph_content">
34 <div class="container_header">
34 <div class="container_header">
35 ${h.form(h.url.current(),method='get')}
35 ${h.form(h.url.current(),method='get')}
36 <div class="info_box">
36 <div class="info_box">
37 ${h.submit('set',_('Show'),class_="ui-button-small")}
37 ${h.submit('set',_('Show'),class_="ui-button-small")}
38 ${h.text('size',size=1,value=c.size)}
38 ${h.text('size',size=1,value=c.size)}
39 <span class="rev">${_('revisions')}</span>
39 <span class="rev">${_('revisions')}</span>
40 </div>
40 </div>
41 ${h.end_form()}
41 ${h.end_form()}
42 <div id="rev_range_container" style="display:none"></div>
42 <div id="rev_range_container" style="display:none"></div>
43 </div>
43 </div>
44
44
45 %for cnt,cs in enumerate(c.pagination):
45 %for cnt,cs in enumerate(c.pagination):
46 <div id="chg_${cnt+1}" class="container">
46 <div id="chg_${cnt+1}" class="container">
47 <div class="left">
47 <div class="left">
48 <div class="date">
48 <div class="date">
49 ${h.checkbox(cs.short_id,class_="changeset_range")}
49 ${h.checkbox(cs.short_id,class_="changeset_range")}
50 <span>${_('commit')} ${cs.revision}: ${h.short_id(cs.raw_id)}@${cs.date}</span>
50 <span>${_('commit')} ${cs.revision}: ${h.short_id(cs.raw_id)}@${cs.date}</span>
51 </div>
51 </div>
52 <div class="author">
52 <div class="author">
53 <div class="gravatar">
53 <div class="gravatar">
54 <img alt="gravatar" src="${h.gravatar_url(h.email(cs.author),20)}"/>
54 <img alt="gravatar" src="${h.gravatar_url(h.email(cs.author),20)}"/>
55 </div>
55 </div>
56 <span>${h.person(cs.author)}</span><br/>
56 <span>${h.person(cs.author)}</span><br/>
57 <span><a href="mailto:${h.email_or_none(cs.author)}">${h.email_or_none(cs.author)}</a></span><br/>
57 <span><a href="mailto:${h.email_or_none(cs.author)}">${h.email_or_none(cs.author)}</a></span><br/>
58 </div>
58 </div>
59 <div class="message">${h.link_to(h.wrap_paragraphs(cs.message),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</div>
59 <div class="message">${h.link_to(h.wrap_paragraphs(cs.message),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</div>
60 </div>
60 </div>
61 <div class="right">
61 <div class="right">
62 <div id="${cs.raw_id}_changes_info" class="changes">
62 <div id="${cs.raw_id}_changes_info" class="changes">
63 <span id="${cs.raw_id}" class="changed_total tooltip"
63 <span id="${cs.raw_id}" class="changed_total tooltip" title="${_('Affected number of files, click to show more details')}">${len(cs.affected_files)}</span>
64 title="${_('Affected number of files, click to show more details')}">
65 ${len(cs.affected_files)}
66 </span>
67 </div>
64 </div>
68 %if len(cs.parents)>1:
65 %if len(cs.parents)>1:
69 <div class="merge">
66 <div class="merge">
70 ${_('merge')}<img alt="merge" src="${h.url('/images/icons/arrow_join.png')}"/>
67 ${_('merge')}<img alt="merge" src="${h.url('/images/icons/arrow_join.png')}"/>
71 </div>
68 </div>
72 %endif
69 %endif
73 %if cs.parents:
70 %if cs.parents:
74 %for p_cs in reversed(cs.parents):
71 %for p_cs in reversed(cs.parents):
75 <div class="parent">${_('Parent')} ${p_cs.revision}: ${h.link_to(h.short_id(p_cs.raw_id),
72 <div class="parent">${_('Parent')} ${p_cs.revision}: ${h.link_to(h.short_id(p_cs.raw_id),
76 h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.raw_id),title=p_cs.message)}
73 h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.raw_id),title=p_cs.message)}
77 </div>
74 </div>
78 %endfor
75 %endfor
79 %else:
76 %else:
80 <div class="parent">${_('No parents')}</div>
77 <div class="parent">${_('No parents')}</div>
81 %endif
78 %endif
82
79
83 <span class="logtags">
80 <span class="logtags">
84 %if cs.branch:
81 %if cs.branch:
85 <span class="branchtag" title="${'%s %s' % (_('branch'),cs.branch)}">
82 <span class="branchtag" title="${'%s %s' % (_('branch'),cs.branch)}">
86 ${h.link_to(cs.branch,h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}</span>
83 ${h.link_to(cs.branch,h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}</span>
87 %endif
84 %endif
88 %for tag in cs.tags:
85 %for tag in cs.tags:
89 <span class="tagtag" title="${'%s %s' % (_('tag'),tag)}">
86 <span class="tagtag" title="${'%s %s' % (_('tag'),tag)}">
90 ${h.link_to(tag,h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}</span>
87 ${h.link_to(tag,h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}</span>
91 %endfor
88 %endfor
92 </span>
89 </span>
93 </div>
90 </div>
94 </div>
91 </div>
95
92
96 %endfor
93 %endfor
97 <div class="pagination-wh pagination-left">
94 <div class="pagination-wh pagination-left">
98 ${c.pagination.pager('$link_previous ~2~ $link_next')}
95 ${c.pagination.pager('$link_previous ~2~ $link_next')}
99 </div>
96 </div>
100 </div>
97 </div>
101 </div>
98 </div>
102
99
103 <script type="text/javascript" src="${h.url('/js/graph.js')}"></script>
100 <script type="text/javascript" src="${h.url('/js/graph.js')}"></script>
104 <script type="text/javascript">
101 <script type="text/javascript">
105 YAHOO.util.Event.onDOMReady(function(){
102 YAHOO.util.Event.onDOMReady(function(){
106
103
107 //Monitor range checkboxes and build a link to changesets
104 //Monitor range checkboxes and build a link to changesets
108 //ranges
105 //ranges
109 var checkboxes = YUD.getElementsByClassName('changeset_range');
106 var checkboxes = YUD.getElementsByClassName('changeset_range');
110 var url_tmpl = "${h.url('changeset_home',repo_name=c.repo_name,revision='__REVRANGE__')}";
107 var url_tmpl = "${h.url('changeset_home',repo_name=c.repo_name,revision='__REVRANGE__')}";
111 YUE.on(checkboxes,'click',function(e){
108 YUE.on(checkboxes,'click',function(e){
112 var checked_checkboxes = [];
109 var checked_checkboxes = [];
113 for (pos in checkboxes){
110 for (pos in checkboxes){
114 if(checkboxes[pos].checked){
111 if(checkboxes[pos].checked){
115 checked_checkboxes.push(checkboxes[pos]);
112 checked_checkboxes.push(checkboxes[pos]);
116 }
113 }
117 }
114 }
118 if(checked_checkboxes.length>1){
115 if(checked_checkboxes.length>1){
119 var rev_end = checked_checkboxes[0].name;
116 var rev_end = checked_checkboxes[0].name;
120 var rev_start = checked_checkboxes[checked_checkboxes.length-1].name;
117 var rev_start = checked_checkboxes[checked_checkboxes.length-1].name;
121
118
122 var url = url_tmpl.replace('__REVRANGE__',
119 var url = url_tmpl.replace('__REVRANGE__',
123 rev_start+'...'+rev_end);
120 rev_start+'...'+rev_end);
124
121
125 var link = "<a href="+url+">${_('Show selected changes __S -> __E')}</a>"
122 var link = "<a href="+url+">${_('Show selected changes __S -> __E')}</a>"
126 link = link.replace('__S',rev_start);
123 link = link.replace('__S',rev_start);
127 link = link.replace('__E',rev_end);
124 link = link.replace('__E',rev_end);
128 YUD.get('rev_range_container').innerHTML = link;
125 YUD.get('rev_range_container').innerHTML = link;
129 YUD.setStyle('rev_range_container','display','');
126 YUD.setStyle('rev_range_container','display','');
130 }
127 }
131 else{
128 else{
132 YUD.setStyle('rev_range_container','display','none');
129 YUD.setStyle('rev_range_container','display','none');
133
130
134 }
131 }
135 });
132 });
136
133
137 //Fetch changeset details
134 //Fetch changeset details
138 YUE.on(YUD.getElementsByClassName('changed_total'),'click',function(e){
135 YUE.on(YUD.getElementsByClassName('changed_total'),'click',function(e){
139 var id = e.currentTarget.id
136 var id = e.currentTarget.id
140 var url = "${h.url('changelog_details',repo_name=c.repo_name,cs='__CS__')}"
137 var url = "${h.url('changelog_details',repo_name=c.repo_name,cs='__CS__')}"
141 var url = url.replace('__CS__',id);
138 var url = url.replace('__CS__',id);
142 ypjax(url,id+'_changes_info',function(){tooltip_activate()});
139 ypjax(url,id+'_changes_info',function(){tooltip_activate()});
143 });
140 });
144
141
145
142
146 function set_canvas(heads) {
143 function set_canvas(heads) {
147 var c = document.getElementById('graph_nodes');
144 var c = document.getElementById('graph_nodes');
148 var t = document.getElementById('graph_content');
145 var t = document.getElementById('graph_content');
149 canvas = document.getElementById('graph_canvas');
146 canvas = document.getElementById('graph_canvas');
150 var div_h = t.clientHeight;
147 var div_h = t.clientHeight;
151 c.style.height=div_h+'px';
148 c.style.height=div_h+'px';
152 canvas.setAttribute('height',div_h);
149 canvas.setAttribute('height',div_h);
153 c.style.height=max_w+'px';
150 c.style.height=max_w+'px';
154 canvas.setAttribute('width',max_w);
151 canvas.setAttribute('width',max_w);
155 };
152 };
156 var heads = 1;
153 var heads = 1;
157 var max_heads = 0;
154 var max_heads = 0;
158 var jsdata = ${c.jsdata|n};
155 var jsdata = ${c.jsdata|n};
159
156
160 for( var i=0;i<jsdata.length;i++){
157 for( var i=0;i<jsdata.length;i++){
161 var m = Math.max.apply(Math, jsdata[i][1]);
158 var m = Math.max.apply(Math, jsdata[i][1]);
162 if (m>max_heads){
159 if (m>max_heads){
163 max_heads = m;
160 max_heads = m;
164 }
161 }
165 }
162 }
166 var max_w = Math.max(100,max_heads*25);
163 var max_w = Math.max(100,max_heads*25);
167 set_canvas(max_w);
164 set_canvas(max_w);
168
165
169 var r = new BranchRenderer();
166 var r = new BranchRenderer();
170 r.render(jsdata,max_w);
167 r.render(jsdata,max_w);
171
168
172 });
169 });
173 </script>
170 </script>
174 %else:
171 %else:
175 ${_('There are no changes yet')}
172 ${_('There are no changes yet')}
176 %endif
173 %endif
177 </div>
174 </div>
178 </div>
175 </div>
179 </%def> No newline at end of file
176 </%def>
@@ -1,70 +1,95 b''
1 from rhodecode.tests import *
1 from rhodecode.tests import *
2 from rhodecode.model.db import UsersGroup
2
3
3 TEST_USERS_GROUP = 'admins_test'
4 TEST_USERS_GROUP = 'admins_test'
4
5
5 class TestAdminUsersGroupsController(TestController):
6 class TestAdminUsersGroupsController(TestController):
6
7
7 def test_index(self):
8 def test_index(self):
8 response = self.app.get(url('users_groups'))
9 response = self.app.get(url('users_groups'))
9 # Test response...
10 # Test response...
10
11
11 def test_index_as_xml(self):
12 def test_index_as_xml(self):
12 response = self.app.get(url('formatted_users_groups', format='xml'))
13 response = self.app.get(url('formatted_users_groups', format='xml'))
13
14
14 def test_create(self):
15 def test_create(self):
15 self.log_user()
16 self.log_user()
16 users_group_name = TEST_USERS_GROUP
17 users_group_name = TEST_USERS_GROUP
17 response = self.app.post(url('users_groups'),
18 response = self.app.post(url('users_groups'),
18 {'users_group_name':users_group_name,
19 {'users_group_name':users_group_name,
19 'active':True})
20 'active':True})
20 response.follow()
21 response.follow()
21
22
22 self.checkSessionFlash(response,
23 self.checkSessionFlash(response,
23 'created users group %s' % TEST_USERS_GROUP)
24 'created users group %s' % TEST_USERS_GROUP)
24
25
25
26
26
27
27
28
28
29
29 def test_new(self):
30 def test_new(self):
30 response = self.app.get(url('new_users_group'))
31 response = self.app.get(url('new_users_group'))
31
32
32 def test_new_as_xml(self):
33 def test_new_as_xml(self):
33 response = self.app.get(url('formatted_new_users_group', format='xml'))
34 response = self.app.get(url('formatted_new_users_group', format='xml'))
34
35
35 def test_update(self):
36 def test_update(self):
36 response = self.app.put(url('users_group', id=1))
37 response = self.app.put(url('users_group', id=1))
37
38
38 def test_update_browser_fakeout(self):
39 def test_update_browser_fakeout(self):
39 response = self.app.post(url('users_group', id=1), params=dict(_method='put'))
40 response = self.app.post(url('users_group', id=1),
41 params=dict(_method='put'))
40
42
41 def test_delete(self):
43 def test_delete(self):
42 response = self.app.delete(url('users_group', id=1))
44 self.log_user()
45 users_group_name = TEST_USERS_GROUP + 'another'
46 response = self.app.post(url('users_groups'),
47 {'users_group_name':users_group_name,
48 'active':True})
49 response.follow()
50
51 self.checkSessionFlash(response,
52 'created users group %s' % users_group_name)
53
54
55 gr = self.sa.query(UsersGroup)\
56 .filter(UsersGroup.users_group_name ==
57 users_group_name).one()
58
59 response = self.app.delete(url('users_group', id=gr.users_group_id))
60
61 gr = self.sa.query(UsersGroup)\
62 .filter(UsersGroup.users_group_name ==
63 users_group_name).scalar()
64
65 self.assertEqual(gr, None)
66
43
67
44 def test_delete_browser_fakeout(self):
68 def test_delete_browser_fakeout(self):
45 response = self.app.post(url('users_group', id=1), params=dict(_method='delete'))
69 response = self.app.post(url('users_group', id=1),
70 params=dict(_method='delete'))
46
71
47 def test_show(self):
72 def test_show(self):
48 response = self.app.get(url('users_group', id=1))
73 response = self.app.get(url('users_group', id=1))
49
74
50 def test_show_as_xml(self):
75 def test_show_as_xml(self):
51 response = self.app.get(url('formatted_users_group', id=1, format='xml'))
76 response = self.app.get(url('formatted_users_group', id=1, format='xml'))
52
77
53 def test_edit(self):
78 def test_edit(self):
54 response = self.app.get(url('edit_users_group', id=1))
79 response = self.app.get(url('edit_users_group', id=1))
55
80
56 def test_edit_as_xml(self):
81 def test_edit_as_xml(self):
57 response = self.app.get(url('formatted_edit_users_group', id=1, format='xml'))
82 response = self.app.get(url('formatted_edit_users_group', id=1, format='xml'))
58
83
59 def test_assign_members(self):
84 def test_assign_members(self):
60 pass
85 pass
61
86
62 def test_add_create_permission(self):
87 def test_add_create_permission(self):
63 pass
88 pass
64
89
65 def test_revoke_members(self):
90 def test_revoke_members(self):
66 pass
91 pass
67
92
68
93
69
94
70
95
@@ -1,41 +1,61 b''
1 from rhodecode.tests import *
1 from rhodecode.tests import *
2
2
3 class TestChangelogController(TestController):
3 class TestChangelogController(TestController):
4
4
5 def test_index_hg(self):
5 def test_index_hg(self):
6 self.log_user()
6 self.log_user()
7 response = self.app.get(url(controller='changelog', action='index', repo_name=HG_REPO))
7 response = self.app.get(url(controller='changelog', action='index',
8 repo_name=HG_REPO))
8
9
9 assert """<div id="chg_20" class="container">""" in response.body, 'wrong info about number of changes'
10 self.assertTrue("""<div id="chg_20" class="container">"""
10 assert """<input class="changeset_range" id="5e204e7583b9" name="5e204e7583b9" type="checkbox" value="1" />""" in response.body, 'no checkbox for this commit'
11 in response.body)
11 assert """<span>commit 154: 5e204e7583b9@2010-08-10 01:18:46</span>""" in response.body , 'no info on this commit'
12 self.assertTrue("""<input class="changeset_range" id="5e204e7583b9" """
12 assert """Small update at simplevcs app""" in response.body, 'missing info about commit message'
13 """name="5e204e7583b9" type="checkbox" value="1" />"""
14 in response.body)
15 self.assertTrue("""<span>commit 154: 5e204e7583b9@2010-08-10 """
16 """01:18:46</span>""" in response.body)
17 self.assertTrue("""Small update at simplevcs app""" in response.body)
13
18
14 assert """<span class="removed tooltip" title="<b>removed</b>: No Files">0</span>""" in response.body, 'wrong info about removed nodes'
19
15 assert """<span class="changed tooltip" title="<b>changed</b>: <br/> vcs/backends/hg.py<br/> vcs/web/simplevcs/models.py">2</span>""" in response.body, 'wrong info about changed nodes'
20 self.assertTrue("""<span id="5e204e7583b9c8e7b93a020bd036564b1e"""
16 assert """<span class="added tooltip" title="<b>added</b>: <br/> vcs/web/simplevcs/managers.py">1</span>""" in response.body, 'wrong info about added nodes'
21 """731dae" class="changed_total tooltip" """
22 """title="Affected number of files, click to """
23 """show more details">3</span>""" in response.body)
17
24
18 #pagination
25 #pagination
19
26
20 response = self.app.get(url(controller='changelog', action='index', repo_name=HG_REPO), {'page':1})
27 response = self.app.get(url(controller='changelog', action='index',
21 response = self.app.get(url(controller='changelog', action='index', repo_name=HG_REPO), {'page':2})
28 repo_name=HG_REPO), {'page':1})
22 response = self.app.get(url(controller='changelog', action='index', repo_name=HG_REPO), {'page':3})
29 response = self.app.get(url(controller='changelog', action='index',
23 response = self.app.get(url(controller='changelog', action='index', repo_name=HG_REPO), {'page':4})
30 repo_name=HG_REPO), {'page':2})
24 response = self.app.get(url(controller='changelog', action='index', repo_name=HG_REPO), {'page':5})
31 response = self.app.get(url(controller='changelog', action='index',
25 response = self.app.get(url(controller='changelog', action='index', repo_name=HG_REPO), {'page':6})
32 repo_name=HG_REPO), {'page':3})
33 response = self.app.get(url(controller='changelog', action='index',
34 repo_name=HG_REPO), {'page':4})
35 response = self.app.get(url(controller='changelog', action='index',
36 repo_name=HG_REPO), {'page':5})
37 response = self.app.get(url(controller='changelog', action='index',
38 repo_name=HG_REPO), {'page':6})
26
39
27
40
28 # Test response after pagination...
41 # Test response after pagination...
29 print response.body
42 self.assertTrue("""<input class="changeset_range" id="46ad32a4f974" """
30 assert """<input class="changeset_range" id="46ad32a4f974" name="46ad32a4f974" type="checkbox" value="1" />""" in response.body, 'no checkbox for this commit'
43 """name="46ad32a4f974" type="checkbox" value="1" />"""
31 assert """<span>commit 64: 46ad32a4f974@2010-04-20 00:33:21</span>"""in response.body, 'wrong info about commit 64'
44 in response.body)
32 assert """<span class="removed tooltip" title="<b>removed</b>: <br/> docs/api.rst">1</span>"""in response.body, 'wrong info about number of removed'
45 self.assertTrue("""<span>commit 64: 46ad32a4f974@2010-04-20"""
33 assert """<span class="changed tooltip" title="<b>changed</b>: <br/> .hgignore<br/> README.rst<br/> docs/conf.py<br/> docs/index.rst<br/> setup.py<br/> tests/test_hg.py<br/> tests/test_nodes.py<br/> vcs/__init__.py<br/> vcs/backends/__init__.py<br/> vcs/backends/base.py<br/> vcs/backends/hg.py<br/> vcs/nodes.py<br/> vcs/utils/__init__.py">13</span>"""in response.body, 'wrong info about number of changes'
46 """ 00:33:21</span>"""in response.body)
34 assert """<span class="added tooltip" title="<b>added</b>: <br/> docs/api/backends/hg.rst<br/> docs/api/backends/index.rst<br/> docs/api/index.rst<br/> docs/api/nodes.rst<br/> docs/api/web/index.rst<br/> docs/api/web/simplevcs.rst<br/> docs/installation.rst<br/> docs/quickstart.rst<br/> setup.cfg<br/> vcs/utils/baseui_config.py<br/> vcs/utils/web.py<br/> vcs/web/__init__.py<br/> vcs/web/exceptions.py<br/> vcs/web/simplevcs/__init__.py<br/> vcs/web/simplevcs/exceptions.py<br/> vcs/web/simplevcs/middleware.py<br/> vcs/web/simplevcs/models.py<br/> vcs/web/simplevcs/settings.py<br/> vcs/web/simplevcs/utils.py<br/> vcs/web/simplevcs/views.py">20</span>"""in response.body, 'wrong info about number of added'
47
35 assert """<div class="message"><a href="/%s/changeset/46ad32a4f974e45472a898c6b0acb600320579b1">Merge with 2e6a2bf9356ca56df08807f4ad86d480da72a8f4</a></div>""" % HG_REPO in response.body, 'wrong info about commit 64 is a merge'
48 self.assertTrue("""<span id="46ad32a4f974e45472a898c6b0acb600320"""
49 """579b1" class="changed_total tooltip" """
50 """title="Affected number of files, click to """
51 """show more details">21</span>"""in response.body)
52 self.assertTrue("""<div class="message"><a href="/%s/changeset/"""
53 """46ad32a4f974e45472a898c6b0acb600320579b1">"""
54 """Merge with 2e6a2bf9356ca56df08807f4ad86d48"""
55 """0da72a8f4</a></div>""" % HG_REPO in response.body)
36
56
37
57
38
58
39 #def test_index_git(self):
59 #def test_index_git(self):
40 # self.log_user()
60 # self.log_user()
41 # response = self.app.get(url(controller='changelog', action='index', repo_name=GIT_REPO))
61 # response = self.app.get(url(controller='changelog', action='index', repo_name=GIT_REPO))
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now