##// END OF EJS Templates
white space cleanup
marcink -
r1963:9bbde542 beta
parent child Browse files
Show More
@@ -1,1173 +1,1173 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.db
3 rhodecode.model.db
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 Database Models for RhodeCode
6 Database Models for RhodeCode
7
7
8 :created_on: Apr 08, 2010
8 :created_on: Apr 08, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import logging
27 import logging
28 import datetime
28 import datetime
29 import traceback
29 import traceback
30 from collections import defaultdict
30 from collections import defaultdict
31
31
32 from sqlalchemy import *
32 from sqlalchemy import *
33 from sqlalchemy.ext.hybrid import hybrid_property
33 from sqlalchemy.ext.hybrid import hybrid_property
34 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
34 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
35 from beaker.cache import cache_region, region_invalidate
35 from beaker.cache import cache_region, region_invalidate
36
36
37 from vcs import get_backend
37 from vcs import get_backend
38 from vcs.utils.helpers import get_scm
38 from vcs.utils.helpers import get_scm
39 from vcs.exceptions import VCSError
39 from vcs.exceptions import VCSError
40 from vcs.utils.lazy import LazyProperty
40 from vcs.utils.lazy import LazyProperty
41
41
42 from rhodecode.lib import str2bool, safe_str, get_changeset_safe, safe_unicode
42 from rhodecode.lib import str2bool, safe_str, get_changeset_safe, safe_unicode
43 from rhodecode.lib.exceptions import UsersGroupsAssignedException
43 from rhodecode.lib.exceptions import UsersGroupsAssignedException
44 from rhodecode.lib.compat import json
44 from rhodecode.lib.compat import json
45 from rhodecode.lib.caching_query import FromCache
45 from rhodecode.lib.caching_query import FromCache
46
46
47 from rhodecode.model.meta import Base, Session
47 from rhodecode.model.meta import Base, Session
48
48
49 log = logging.getLogger(__name__)
49 log = logging.getLogger(__name__)
50
50
51 #==============================================================================
51 #==============================================================================
52 # BASE CLASSES
52 # BASE CLASSES
53 #==============================================================================
53 #==============================================================================
54
54
55
55
56 class ModelSerializer(json.JSONEncoder):
56 class ModelSerializer(json.JSONEncoder):
57 """
57 """
58 Simple Serializer for JSON,
58 Simple Serializer for JSON,
59
59
60 usage::
60 usage::
61
61
62 to make object customized for serialization implement a __json__
62 to make object customized for serialization implement a __json__
63 method that will return a dict for serialization into json
63 method that will return a dict for serialization into json
64
64
65 example::
65 example::
66
66
67 class Task(object):
67 class Task(object):
68
68
69 def __init__(self, name, value):
69 def __init__(self, name, value):
70 self.name = name
70 self.name = name
71 self.value = value
71 self.value = value
72
72
73 def __json__(self):
73 def __json__(self):
74 return dict(name=self.name,
74 return dict(name=self.name,
75 value=self.value)
75 value=self.value)
76
76
77 """
77 """
78
78
79 def default(self, obj):
79 def default(self, obj):
80
80
81 if hasattr(obj, '__json__'):
81 if hasattr(obj, '__json__'):
82 return obj.__json__()
82 return obj.__json__()
83 else:
83 else:
84 return json.JSONEncoder.default(self, obj)
84 return json.JSONEncoder.default(self, obj)
85
85
86
86
87 class BaseModel(object):
87 class BaseModel(object):
88 """
88 """
89 Base Model for all classess
89 Base Model for all classess
90 """
90 """
91
91
92 @classmethod
92 @classmethod
93 def _get_keys(cls):
93 def _get_keys(cls):
94 """return column names for this model """
94 """return column names for this model """
95 return class_mapper(cls).c.keys()
95 return class_mapper(cls).c.keys()
96
96
97 def get_dict(self):
97 def get_dict(self):
98 """
98 """
99 return dict with keys and values corresponding
99 return dict with keys and values corresponding
100 to this model data """
100 to this model data """
101
101
102 d = {}
102 d = {}
103 for k in self._get_keys():
103 for k in self._get_keys():
104 d[k] = getattr(self, k)
104 d[k] = getattr(self, k)
105
105
106 # also use __json__() if present to get additional fields
106 # also use __json__() if present to get additional fields
107 for k, val in getattr(self, '__json__', lambda: {})().iteritems():
107 for k, val in getattr(self, '__json__', lambda: {})().iteritems():
108 d[k] = val
108 d[k] = val
109 return d
109 return d
110
110
111 def get_appstruct(self):
111 def get_appstruct(self):
112 """return list with keys and values tupples corresponding
112 """return list with keys and values tupples corresponding
113 to this model data """
113 to this model data """
114
114
115 l = []
115 l = []
116 for k in self._get_keys():
116 for k in self._get_keys():
117 l.append((k, getattr(self, k),))
117 l.append((k, getattr(self, k),))
118 return l
118 return l
119
119
120 def populate_obj(self, populate_dict):
120 def populate_obj(self, populate_dict):
121 """populate model with data from given populate_dict"""
121 """populate model with data from given populate_dict"""
122
122
123 for k in self._get_keys():
123 for k in self._get_keys():
124 if k in populate_dict:
124 if k in populate_dict:
125 setattr(self, k, populate_dict[k])
125 setattr(self, k, populate_dict[k])
126
126
127 @classmethod
127 @classmethod
128 def query(cls):
128 def query(cls):
129 return Session.query(cls)
129 return Session.query(cls)
130
130
131 @classmethod
131 @classmethod
132 def get(cls, id_):
132 def get(cls, id_):
133 if id_:
133 if id_:
134 return cls.query().get(id_)
134 return cls.query().get(id_)
135
135
136 @classmethod
136 @classmethod
137 def getAll(cls):
137 def getAll(cls):
138 return cls.query().all()
138 return cls.query().all()
139
139
140 @classmethod
140 @classmethod
141 def delete(cls, id_):
141 def delete(cls, id_):
142 obj = cls.query().get(id_)
142 obj = cls.query().get(id_)
143 Session.delete(obj)
143 Session.delete(obj)
144
144
145
145
146 class RhodeCodeSetting(Base, BaseModel):
146 class RhodeCodeSetting(Base, BaseModel):
147 __tablename__ = 'rhodecode_settings'
147 __tablename__ = 'rhodecode_settings'
148 __table_args__ = (
148 __table_args__ = (
149 UniqueConstraint('app_settings_name'),
149 UniqueConstraint('app_settings_name'),
150 {'extend_existing': True}
150 {'extend_existing': True}
151 )
151 )
152 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
152 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
153 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
153 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
154 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
154 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
155
155
156 def __init__(self, k='', v=''):
156 def __init__(self, k='', v=''):
157 self.app_settings_name = k
157 self.app_settings_name = k
158 self.app_settings_value = v
158 self.app_settings_value = v
159
159
160 @validates('_app_settings_value')
160 @validates('_app_settings_value')
161 def validate_settings_value(self, key, val):
161 def validate_settings_value(self, key, val):
162 assert type(val) == unicode
162 assert type(val) == unicode
163 return val
163 return val
164
164
165 @hybrid_property
165 @hybrid_property
166 def app_settings_value(self):
166 def app_settings_value(self):
167 v = self._app_settings_value
167 v = self._app_settings_value
168 if v == 'ldap_active':
168 if v == 'ldap_active':
169 v = str2bool(v)
169 v = str2bool(v)
170 return v
170 return v
171
171
172 @app_settings_value.setter
172 @app_settings_value.setter
173 def app_settings_value(self, val):
173 def app_settings_value(self, val):
174 """
174 """
175 Setter that will always make sure we use unicode in app_settings_value
175 Setter that will always make sure we use unicode in app_settings_value
176
176
177 :param val:
177 :param val:
178 """
178 """
179 self._app_settings_value = safe_unicode(val)
179 self._app_settings_value = safe_unicode(val)
180
180
181 def __repr__(self):
181 def __repr__(self):
182 return "<%s('%s:%s')>" % (
182 return "<%s('%s:%s')>" % (
183 self.__class__.__name__,
183 self.__class__.__name__,
184 self.app_settings_name, self.app_settings_value
184 self.app_settings_name, self.app_settings_value
185 )
185 )
186
186
187 @classmethod
187 @classmethod
188 def get_by_name(cls, ldap_key):
188 def get_by_name(cls, ldap_key):
189 return cls.query()\
189 return cls.query()\
190 .filter(cls.app_settings_name == ldap_key).scalar()
190 .filter(cls.app_settings_name == ldap_key).scalar()
191
191
192 @classmethod
192 @classmethod
193 def get_app_settings(cls, cache=False):
193 def get_app_settings(cls, cache=False):
194
194
195 ret = cls.query()
195 ret = cls.query()
196
196
197 if cache:
197 if cache:
198 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
198 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
199
199
200 if not ret:
200 if not ret:
201 raise Exception('Could not get application settings !')
201 raise Exception('Could not get application settings !')
202 settings = {}
202 settings = {}
203 for each in ret:
203 for each in ret:
204 settings['rhodecode_' + each.app_settings_name] = \
204 settings['rhodecode_' + each.app_settings_name] = \
205 each.app_settings_value
205 each.app_settings_value
206
206
207 return settings
207 return settings
208
208
209 @classmethod
209 @classmethod
210 def get_ldap_settings(cls, cache=False):
210 def get_ldap_settings(cls, cache=False):
211 ret = cls.query()\
211 ret = cls.query()\
212 .filter(cls.app_settings_name.startswith('ldap_')).all()
212 .filter(cls.app_settings_name.startswith('ldap_')).all()
213 fd = {}
213 fd = {}
214 for row in ret:
214 for row in ret:
215 fd.update({row.app_settings_name:row.app_settings_value})
215 fd.update({row.app_settings_name:row.app_settings_value})
216
216
217 return fd
217 return fd
218
218
219
219
220 class RhodeCodeUi(Base, BaseModel):
220 class RhodeCodeUi(Base, BaseModel):
221 __tablename__ = 'rhodecode_ui'
221 __tablename__ = 'rhodecode_ui'
222 __table_args__ = (
222 __table_args__ = (
223 UniqueConstraint('ui_key'),
223 UniqueConstraint('ui_key'),
224 {'extend_existing': True}
224 {'extend_existing': True}
225 )
225 )
226
226
227 HOOK_UPDATE = 'changegroup.update'
227 HOOK_UPDATE = 'changegroup.update'
228 HOOK_REPO_SIZE = 'changegroup.repo_size'
228 HOOK_REPO_SIZE = 'changegroup.repo_size'
229 HOOK_PUSH = 'pretxnchangegroup.push_logger'
229 HOOK_PUSH = 'pretxnchangegroup.push_logger'
230 HOOK_PULL = 'preoutgoing.pull_logger'
230 HOOK_PULL = 'preoutgoing.pull_logger'
231
231
232 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
232 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
233 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
233 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
234 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
234 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
235 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
235 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
236 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
236 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
237
237
238 @classmethod
238 @classmethod
239 def get_by_key(cls, key):
239 def get_by_key(cls, key):
240 return cls.query().filter(cls.ui_key == key)
240 return cls.query().filter(cls.ui_key == key)
241
241
242 @classmethod
242 @classmethod
243 def get_builtin_hooks(cls):
243 def get_builtin_hooks(cls):
244 q = cls.query()
244 q = cls.query()
245 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
245 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
246 cls.HOOK_REPO_SIZE,
246 cls.HOOK_REPO_SIZE,
247 cls.HOOK_PUSH, cls.HOOK_PULL]))
247 cls.HOOK_PUSH, cls.HOOK_PULL]))
248 return q.all()
248 return q.all()
249
249
250 @classmethod
250 @classmethod
251 def get_custom_hooks(cls):
251 def get_custom_hooks(cls):
252 q = cls.query()
252 q = cls.query()
253 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
253 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
254 cls.HOOK_REPO_SIZE,
254 cls.HOOK_REPO_SIZE,
255 cls.HOOK_PUSH, cls.HOOK_PULL]))
255 cls.HOOK_PUSH, cls.HOOK_PULL]))
256 q = q.filter(cls.ui_section == 'hooks')
256 q = q.filter(cls.ui_section == 'hooks')
257 return q.all()
257 return q.all()
258
258
259 @classmethod
259 @classmethod
260 def create_or_update_hook(cls, key, val):
260 def create_or_update_hook(cls, key, val):
261 new_ui = cls.get_by_key(key).scalar() or cls()
261 new_ui = cls.get_by_key(key).scalar() or cls()
262 new_ui.ui_section = 'hooks'
262 new_ui.ui_section = 'hooks'
263 new_ui.ui_active = True
263 new_ui.ui_active = True
264 new_ui.ui_key = key
264 new_ui.ui_key = key
265 new_ui.ui_value = val
265 new_ui.ui_value = val
266
266
267 Session.add(new_ui)
267 Session.add(new_ui)
268
268
269
269
270 class User(Base, BaseModel):
270 class User(Base, BaseModel):
271 __tablename__ = 'users'
271 __tablename__ = 'users'
272 __table_args__ = (
272 __table_args__ = (
273 UniqueConstraint('username'), UniqueConstraint('email'),
273 UniqueConstraint('username'), UniqueConstraint('email'),
274 {'extend_existing': True}
274 {'extend_existing': True}
275 )
275 )
276 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
276 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
277 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
277 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
278 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
278 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
279 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
279 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
280 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
280 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
281 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
281 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
282 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
282 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
283 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
283 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
284 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
284 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
285 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
285 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
286 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
286 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
287
287
288 user_log = relationship('UserLog', cascade='all')
288 user_log = relationship('UserLog', cascade='all')
289 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
289 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
290
290
291 repositories = relationship('Repository')
291 repositories = relationship('Repository')
292 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
292 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
293 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
293 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
294
294
295 group_member = relationship('UsersGroupMember', cascade='all')
295 group_member = relationship('UsersGroupMember', cascade='all')
296
296
297 notifications = relationship('UserNotification',)
297 notifications = relationship('UserNotification',)
298
298
299 @hybrid_property
299 @hybrid_property
300 def email(self):
300 def email(self):
301 return self._email
301 return self._email
302
302
303 @email.setter
303 @email.setter
304 def email(self, val):
304 def email(self, val):
305 self._email = val.lower() if val else None
305 self._email = val.lower() if val else None
306
306
307 @property
307 @property
308 def full_name(self):
308 def full_name(self):
309 return '%s %s' % (self.name, self.lastname)
309 return '%s %s' % (self.name, self.lastname)
310
310
311 @property
311 @property
312 def full_name_or_username(self):
312 def full_name_or_username(self):
313 return ('%s %s' % (self.name, self.lastname)
313 return ('%s %s' % (self.name, self.lastname)
314 if (self.name and self.lastname) else self.username)
314 if (self.name and self.lastname) else self.username)
315
315
316 @property
316 @property
317 def full_contact(self):
317 def full_contact(self):
318 return '%s %s <%s>' % (self.name, self.lastname, self.email)
318 return '%s %s <%s>' % (self.name, self.lastname, self.email)
319
319
320 @property
320 @property
321 def short_contact(self):
321 def short_contact(self):
322 return '%s %s' % (self.name, self.lastname)
322 return '%s %s' % (self.name, self.lastname)
323
323
324 @property
324 @property
325 def is_admin(self):
325 def is_admin(self):
326 return self.admin
326 return self.admin
327
327
328 def __repr__(self):
328 def __repr__(self):
329 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
329 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
330 self.user_id, self.username)
330 self.user_id, self.username)
331
331
332 @classmethod
332 @classmethod
333 def get_by_username(cls, username, case_insensitive=False, cache=False):
333 def get_by_username(cls, username, case_insensitive=False, cache=False):
334 if case_insensitive:
334 if case_insensitive:
335 q = cls.query().filter(cls.username.ilike(username))
335 q = cls.query().filter(cls.username.ilike(username))
336 else:
336 else:
337 q = cls.query().filter(cls.username == username)
337 q = cls.query().filter(cls.username == username)
338
338
339 if cache:
339 if cache:
340 q = q.options(FromCache("sql_cache_short",
340 q = q.options(FromCache("sql_cache_short",
341 "get_user_%s" % username))
341 "get_user_%s" % username))
342 return q.scalar()
342 return q.scalar()
343
343
344 @classmethod
344 @classmethod
345 def get_by_api_key(cls, api_key, cache=False):
345 def get_by_api_key(cls, api_key, cache=False):
346 q = cls.query().filter(cls.api_key == api_key)
346 q = cls.query().filter(cls.api_key == api_key)
347
347
348 if cache:
348 if cache:
349 q = q.options(FromCache("sql_cache_short",
349 q = q.options(FromCache("sql_cache_short",
350 "get_api_key_%s" % api_key))
350 "get_api_key_%s" % api_key))
351 return q.scalar()
351 return q.scalar()
352
352
353 @classmethod
353 @classmethod
354 def get_by_email(cls, email, case_insensitive=False, cache=False):
354 def get_by_email(cls, email, case_insensitive=False, cache=False):
355 if case_insensitive:
355 if case_insensitive:
356 q = cls.query().filter(cls.email.ilike(email))
356 q = cls.query().filter(cls.email.ilike(email))
357 else:
357 else:
358 q = cls.query().filter(cls.email == email)
358 q = cls.query().filter(cls.email == email)
359
359
360 if cache:
360 if cache:
361 q = q.options(FromCache("sql_cache_short",
361 q = q.options(FromCache("sql_cache_short",
362 "get_api_key_%s" % email))
362 "get_api_key_%s" % email))
363 return q.scalar()
363 return q.scalar()
364
364
365 def update_lastlogin(self):
365 def update_lastlogin(self):
366 """Update user lastlogin"""
366 """Update user lastlogin"""
367 self.last_login = datetime.datetime.now()
367 self.last_login = datetime.datetime.now()
368 Session.add(self)
368 Session.add(self)
369 log.debug('updated user %s lastlogin', self.username)
369 log.debug('updated user %s lastlogin', self.username)
370
370
371 def __json__(self):
371 def __json__(self):
372 return dict(
372 return dict(
373 email=self.email,
373 email=self.email,
374 full_name=self.full_name,
374 full_name=self.full_name,
375 full_name_or_username=self.full_name_or_username,
375 full_name_or_username=self.full_name_or_username,
376 short_contact=self.short_contact,
376 short_contact=self.short_contact,
377 full_contact=self.full_contact
377 full_contact=self.full_contact
378 )
378 )
379
379
380
380
381 class UserLog(Base, BaseModel):
381 class UserLog(Base, BaseModel):
382 __tablename__ = 'user_logs'
382 __tablename__ = 'user_logs'
383 __table_args__ = {'extend_existing': True}
383 __table_args__ = {'extend_existing': True}
384 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
384 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
385 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
385 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
386 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
386 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
387 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
387 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
388 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
388 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
389 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
389 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
390 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
390 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
391
391
392 @property
392 @property
393 def action_as_day(self):
393 def action_as_day(self):
394 return datetime.date(*self.action_date.timetuple()[:3])
394 return datetime.date(*self.action_date.timetuple()[:3])
395
395
396 user = relationship('User')
396 user = relationship('User')
397 repository = relationship('Repository',cascade='')
397 repository = relationship('Repository',cascade='')
398
398
399
399
400 class UsersGroup(Base, BaseModel):
400 class UsersGroup(Base, BaseModel):
401 __tablename__ = 'users_groups'
401 __tablename__ = 'users_groups'
402 __table_args__ = {'extend_existing': True}
402 __table_args__ = {'extend_existing': True}
403
403
404 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
404 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
405 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
405 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
406 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
406 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
407
407
408 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
408 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
409
409
410 def __repr__(self):
410 def __repr__(self):
411 return '<userGroup(%s)>' % (self.users_group_name)
411 return '<userGroup(%s)>' % (self.users_group_name)
412
412
413 @classmethod
413 @classmethod
414 def get_by_group_name(cls, group_name, cache=False,
414 def get_by_group_name(cls, group_name, cache=False,
415 case_insensitive=False):
415 case_insensitive=False):
416 if case_insensitive:
416 if case_insensitive:
417 q = cls.query().filter(cls.users_group_name.ilike(group_name))
417 q = cls.query().filter(cls.users_group_name.ilike(group_name))
418 else:
418 else:
419 q = cls.query().filter(cls.users_group_name == group_name)
419 q = cls.query().filter(cls.users_group_name == group_name)
420 if cache:
420 if cache:
421 q = q.options(FromCache("sql_cache_short",
421 q = q.options(FromCache("sql_cache_short",
422 "get_user_%s" % group_name))
422 "get_user_%s" % group_name))
423 return q.scalar()
423 return q.scalar()
424
424
425 @classmethod
425 @classmethod
426 def get(cls, users_group_id, cache=False):
426 def get(cls, users_group_id, cache=False):
427 users_group = cls.query()
427 users_group = cls.query()
428 if cache:
428 if cache:
429 users_group = users_group.options(FromCache("sql_cache_short",
429 users_group = users_group.options(FromCache("sql_cache_short",
430 "get_users_group_%s" % users_group_id))
430 "get_users_group_%s" % users_group_id))
431 return users_group.get(users_group_id)
431 return users_group.get(users_group_id)
432
432
433
433
434 class UsersGroupMember(Base, BaseModel):
434 class UsersGroupMember(Base, BaseModel):
435 __tablename__ = 'users_groups_members'
435 __tablename__ = 'users_groups_members'
436 __table_args__ = {'extend_existing': True}
436 __table_args__ = {'extend_existing': True}
437
437
438 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
438 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
439 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
439 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
440 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
440 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
441
441
442 user = relationship('User', lazy='joined')
442 user = relationship('User', lazy='joined')
443 users_group = relationship('UsersGroup')
443 users_group = relationship('UsersGroup')
444
444
445 def __init__(self, gr_id='', u_id=''):
445 def __init__(self, gr_id='', u_id=''):
446 self.users_group_id = gr_id
446 self.users_group_id = gr_id
447 self.user_id = u_id
447 self.user_id = u_id
448
448
449 @staticmethod
449 @staticmethod
450 def add_user_to_group(group, user):
450 def add_user_to_group(group, user):
451 ugm = UsersGroupMember()
451 ugm = UsersGroupMember()
452 ugm.users_group = group
452 ugm.users_group = group
453 ugm.user = user
453 ugm.user = user
454 Session.add(ugm)
454 Session.add(ugm)
455 Session.commit()
455 Session.commit()
456 return ugm
456 return ugm
457
457
458
458
459 class Repository(Base, BaseModel):
459 class Repository(Base, BaseModel):
460 __tablename__ = 'repositories'
460 __tablename__ = 'repositories'
461 __table_args__ = (
461 __table_args__ = (
462 UniqueConstraint('repo_name'),
462 UniqueConstraint('repo_name'),
463 {'extend_existing': True},
463 {'extend_existing': True},
464 )
464 )
465
465
466 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
466 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
467 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
467 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
468 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
468 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
469 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
469 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
470 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
470 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
471 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
471 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
472 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
472 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
473 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
473 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
474 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
474 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
475 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
475 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
476
476
477 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
477 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
478 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
478 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
479
479
480 user = relationship('User')
480 user = relationship('User')
481 fork = relationship('Repository', remote_side=repo_id)
481 fork = relationship('Repository', remote_side=repo_id)
482 group = relationship('RepoGroup')
482 group = relationship('RepoGroup')
483 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
483 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
484 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
484 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
485 stats = relationship('Statistics', cascade='all', uselist=False)
485 stats = relationship('Statistics', cascade='all', uselist=False)
486
486
487 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
487 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
488
488
489 logs = relationship('UserLog')
489 logs = relationship('UserLog')
490
490
491 def __repr__(self):
491 def __repr__(self):
492 return "<%s('%s:%s')>" % (self.__class__.__name__,
492 return "<%s('%s:%s')>" % (self.__class__.__name__,
493 self.repo_id, self.repo_name)
493 self.repo_id, self.repo_name)
494
494
495 @classmethod
495 @classmethod
496 def url_sep(cls):
496 def url_sep(cls):
497 return '/'
497 return '/'
498
498
499 @classmethod
499 @classmethod
500 def get_by_repo_name(cls, repo_name):
500 def get_by_repo_name(cls, repo_name):
501 q = Session.query(cls).filter(cls.repo_name == repo_name)
501 q = Session.query(cls).filter(cls.repo_name == repo_name)
502 q = q.options(joinedload(Repository.fork))\
502 q = q.options(joinedload(Repository.fork))\
503 .options(joinedload(Repository.user))\
503 .options(joinedload(Repository.user))\
504 .options(joinedload(Repository.group))
504 .options(joinedload(Repository.group))
505 return q.scalar()
505 return q.scalar()
506
506
507 @classmethod
507 @classmethod
508 def get_repo_forks(cls, repo_id):
508 def get_repo_forks(cls, repo_id):
509 return cls.query().filter(Repository.fork_id == repo_id)
509 return cls.query().filter(Repository.fork_id == repo_id)
510
510
511 @classmethod
511 @classmethod
512 def base_path(cls):
512 def base_path(cls):
513 """
513 """
514 Returns base path when all repos are stored
514 Returns base path when all repos are stored
515
515
516 :param cls:
516 :param cls:
517 """
517 """
518 q = Session.query(RhodeCodeUi)\
518 q = Session.query(RhodeCodeUi)\
519 .filter(RhodeCodeUi.ui_key == cls.url_sep())
519 .filter(RhodeCodeUi.ui_key == cls.url_sep())
520 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
520 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
521 return q.one().ui_value
521 return q.one().ui_value
522
522
523 @property
523 @property
524 def just_name(self):
524 def just_name(self):
525 return self.repo_name.split(Repository.url_sep())[-1]
525 return self.repo_name.split(Repository.url_sep())[-1]
526
526
527 @property
527 @property
528 def groups_with_parents(self):
528 def groups_with_parents(self):
529 groups = []
529 groups = []
530 if self.group is None:
530 if self.group is None:
531 return groups
531 return groups
532
532
533 cur_gr = self.group
533 cur_gr = self.group
534 groups.insert(0, cur_gr)
534 groups.insert(0, cur_gr)
535 while 1:
535 while 1:
536 gr = getattr(cur_gr, 'parent_group', None)
536 gr = getattr(cur_gr, 'parent_group', None)
537 cur_gr = cur_gr.parent_group
537 cur_gr = cur_gr.parent_group
538 if gr is None:
538 if gr is None:
539 break
539 break
540 groups.insert(0, gr)
540 groups.insert(0, gr)
541
541
542 return groups
542 return groups
543
543
544 @property
544 @property
545 def groups_and_repo(self):
545 def groups_and_repo(self):
546 return self.groups_with_parents, self.just_name
546 return self.groups_with_parents, self.just_name
547
547
548 @LazyProperty
548 @LazyProperty
549 def repo_path(self):
549 def repo_path(self):
550 """
550 """
551 Returns base full path for that repository means where it actually
551 Returns base full path for that repository means where it actually
552 exists on a filesystem
552 exists on a filesystem
553 """
553 """
554 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
554 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
555 Repository.url_sep())
555 Repository.url_sep())
556 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
556 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
557 return q.one().ui_value
557 return q.one().ui_value
558
558
559 @property
559 @property
560 def repo_full_path(self):
560 def repo_full_path(self):
561 p = [self.repo_path]
561 p = [self.repo_path]
562 # we need to split the name by / since this is how we store the
562 # we need to split the name by / since this is how we store the
563 # names in the database, but that eventually needs to be converted
563 # names in the database, but that eventually needs to be converted
564 # into a valid system path
564 # into a valid system path
565 p += self.repo_name.split(Repository.url_sep())
565 p += self.repo_name.split(Repository.url_sep())
566 return os.path.join(*p)
566 return os.path.join(*p)
567
567
568 def get_new_name(self, repo_name):
568 def get_new_name(self, repo_name):
569 """
569 """
570 returns new full repository name based on assigned group and new new
570 returns new full repository name based on assigned group and new new
571
571
572 :param group_name:
572 :param group_name:
573 """
573 """
574 path_prefix = self.group.full_path_splitted if self.group else []
574 path_prefix = self.group.full_path_splitted if self.group else []
575 return Repository.url_sep().join(path_prefix + [repo_name])
575 return Repository.url_sep().join(path_prefix + [repo_name])
576
576
577 @property
577 @property
578 def _ui(self):
578 def _ui(self):
579 """
579 """
580 Creates an db based ui object for this repository
580 Creates an db based ui object for this repository
581 """
581 """
582 from mercurial import ui
582 from mercurial import ui
583 from mercurial import config
583 from mercurial import config
584 baseui = ui.ui()
584 baseui = ui.ui()
585
585
586 #clean the baseui object
586 #clean the baseui object
587 baseui._ocfg = config.config()
587 baseui._ocfg = config.config()
588 baseui._ucfg = config.config()
588 baseui._ucfg = config.config()
589 baseui._tcfg = config.config()
589 baseui._tcfg = config.config()
590
590
591 ret = RhodeCodeUi.query()\
591 ret = RhodeCodeUi.query()\
592 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
592 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
593
593
594 hg_ui = ret
594 hg_ui = ret
595 for ui_ in hg_ui:
595 for ui_ in hg_ui:
596 if ui_.ui_active:
596 if ui_.ui_active:
597 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
597 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
598 ui_.ui_key, ui_.ui_value)
598 ui_.ui_key, ui_.ui_value)
599 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
599 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
600
600
601 return baseui
601 return baseui
602
602
603 @classmethod
603 @classmethod
604 def is_valid(cls, repo_name):
604 def is_valid(cls, repo_name):
605 """
605 """
606 returns True if given repo name is a valid filesystem repository
606 returns True if given repo name is a valid filesystem repository
607
607
608 :param cls:
608 :param cls:
609 :param repo_name:
609 :param repo_name:
610 """
610 """
611 from rhodecode.lib.utils import is_valid_repo
611 from rhodecode.lib.utils import is_valid_repo
612
612
613 return is_valid_repo(repo_name, cls.base_path())
613 return is_valid_repo(repo_name, cls.base_path())
614
614
615 #==========================================================================
615 #==========================================================================
616 # SCM PROPERTIES
616 # SCM PROPERTIES
617 #==========================================================================
617 #==========================================================================
618
618
619 def get_changeset(self, rev):
619 def get_changeset(self, rev):
620 return get_changeset_safe(self.scm_instance, rev)
620 return get_changeset_safe(self.scm_instance, rev)
621
621
622 @property
622 @property
623 def tip(self):
623 def tip(self):
624 return self.get_changeset('tip')
624 return self.get_changeset('tip')
625
625
626 @property
626 @property
627 def author(self):
627 def author(self):
628 return self.tip.author
628 return self.tip.author
629
629
630 @property
630 @property
631 def last_change(self):
631 def last_change(self):
632 return self.scm_instance.last_change
632 return self.scm_instance.last_change
633
633
634 def comments(self, revisions=None):
634 def comments(self, revisions=None):
635 """
635 """
636 Returns comments for this repository grouped by revisions
636 Returns comments for this repository grouped by revisions
637
637
638 :param revisions: filter query by revisions only
638 :param revisions: filter query by revisions only
639 """
639 """
640 cmts = ChangesetComment.query()\
640 cmts = ChangesetComment.query()\
641 .filter(ChangesetComment.repo == self)
641 .filter(ChangesetComment.repo == self)
642 if revisions:
642 if revisions:
643 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
643 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
644 grouped = defaultdict(list)
644 grouped = defaultdict(list)
645 for cmt in cmts.all():
645 for cmt in cmts.all():
646 grouped[cmt.revision].append(cmt)
646 grouped[cmt.revision].append(cmt)
647 return grouped
647 return grouped
648
648
649 #==========================================================================
649 #==========================================================================
650 # SCM CACHE INSTANCE
650 # SCM CACHE INSTANCE
651 #==========================================================================
651 #==========================================================================
652
652
653 @property
653 @property
654 def invalidate(self):
654 def invalidate(self):
655 return CacheInvalidation.invalidate(self.repo_name)
655 return CacheInvalidation.invalidate(self.repo_name)
656
656
657 def set_invalidate(self):
657 def set_invalidate(self):
658 """
658 """
659 set a cache for invalidation for this instance
659 set a cache for invalidation for this instance
660 """
660 """
661 CacheInvalidation.set_invalidate(self.repo_name)
661 CacheInvalidation.set_invalidate(self.repo_name)
662
662
663 @LazyProperty
663 @LazyProperty
664 def scm_instance(self):
664 def scm_instance(self):
665 return self.__get_instance()
665 return self.__get_instance()
666
666
667 @property
667 @property
668 def scm_instance_cached(self):
668 def scm_instance_cached(self):
669 @cache_region('long_term')
669 @cache_region('long_term')
670 def _c(repo_name):
670 def _c(repo_name):
671 return self.__get_instance()
671 return self.__get_instance()
672 rn = self.repo_name
672 rn = self.repo_name
673 log.debug('Getting cached instance of repo')
673 log.debug('Getting cached instance of repo')
674 inv = self.invalidate
674 inv = self.invalidate
675 if inv is not None:
675 if inv is not None:
676 region_invalidate(_c, None, rn)
676 region_invalidate(_c, None, rn)
677 # update our cache
677 # update our cache
678 CacheInvalidation.set_valid(inv.cache_key)
678 CacheInvalidation.set_valid(inv.cache_key)
679 return _c(rn)
679 return _c(rn)
680
680
681 def __get_instance(self):
681 def __get_instance(self):
682 repo_full_path = self.repo_full_path
682 repo_full_path = self.repo_full_path
683 try:
683 try:
684 alias = get_scm(repo_full_path)[0]
684 alias = get_scm(repo_full_path)[0]
685 log.debug('Creating instance of %s repository', alias)
685 log.debug('Creating instance of %s repository', alias)
686 backend = get_backend(alias)
686 backend = get_backend(alias)
687 except VCSError:
687 except VCSError:
688 log.error(traceback.format_exc())
688 log.error(traceback.format_exc())
689 log.error('Perhaps this repository is in db and not in '
689 log.error('Perhaps this repository is in db and not in '
690 'filesystem run rescan repositories with '
690 'filesystem run rescan repositories with '
691 '"destroy old data " option from admin panel')
691 '"destroy old data " option from admin panel')
692 return
692 return
693
693
694 if alias == 'hg':
694 if alias == 'hg':
695 repo = backend(safe_str(repo_full_path), create=False,
695 repo = backend(safe_str(repo_full_path), create=False,
696 baseui=self._ui)
696 baseui=self._ui)
697 # skip hidden web repository
697 # skip hidden web repository
698 if repo._get_hidden():
698 if repo._get_hidden():
699 return
699 return
700 else:
700 else:
701 repo = backend(repo_full_path, create=False)
701 repo = backend(repo_full_path, create=False)
702
702
703 return repo
703 return repo
704
704
705
705
706 class RepoGroup(Base, BaseModel):
706 class RepoGroup(Base, BaseModel):
707 __tablename__ = 'groups'
707 __tablename__ = 'groups'
708 __table_args__ = (
708 __table_args__ = (
709 UniqueConstraint('group_name', 'group_parent_id'),
709 UniqueConstraint('group_name', 'group_parent_id'),
710 CheckConstraint('group_id != group_parent_id'),
710 CheckConstraint('group_id != group_parent_id'),
711 {'extend_existing': True},
711 {'extend_existing': True},
712 )
712 )
713 __mapper_args__ = {'order_by': 'group_name'}
713 __mapper_args__ = {'order_by': 'group_name'}
714
714
715 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
715 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
716 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
716 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
717 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
717 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
718 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
718 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
719
719
720 parent_group = relationship('RepoGroup', remote_side=group_id)
720 parent_group = relationship('RepoGroup', remote_side=group_id)
721
721
722 def __init__(self, group_name='', parent_group=None):
722 def __init__(self, group_name='', parent_group=None):
723 self.group_name = group_name
723 self.group_name = group_name
724 self.parent_group = parent_group
724 self.parent_group = parent_group
725
725
726 def __repr__(self):
726 def __repr__(self):
727 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
727 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
728 self.group_name)
728 self.group_name)
729
729
730 @classmethod
730 @classmethod
731 def groups_choices(cls):
731 def groups_choices(cls):
732 from webhelpers.html import literal as _literal
732 from webhelpers.html import literal as _literal
733 repo_groups = [('', '')]
733 repo_groups = [('', '')]
734 sep = ' &raquo; '
734 sep = ' &raquo; '
735 _name = lambda k: _literal(sep.join(k))
735 _name = lambda k: _literal(sep.join(k))
736
736
737 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
737 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
738 for x in cls.query().all()])
738 for x in cls.query().all()])
739
739
740 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
740 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
741 return repo_groups
741 return repo_groups
742
742
743 @classmethod
743 @classmethod
744 def url_sep(cls):
744 def url_sep(cls):
745 return '/'
745 return '/'
746
746
747 @classmethod
747 @classmethod
748 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
748 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
749 if case_insensitive:
749 if case_insensitive:
750 gr = cls.query()\
750 gr = cls.query()\
751 .filter(cls.group_name.ilike(group_name))
751 .filter(cls.group_name.ilike(group_name))
752 else:
752 else:
753 gr = cls.query()\
753 gr = cls.query()\
754 .filter(cls.group_name == group_name)
754 .filter(cls.group_name == group_name)
755 if cache:
755 if cache:
756 gr = gr.options(FromCache("sql_cache_short",
756 gr = gr.options(FromCache("sql_cache_short",
757 "get_group_%s" % group_name))
757 "get_group_%s" % group_name))
758 return gr.scalar()
758 return gr.scalar()
759
759
760 @property
760 @property
761 def parents(self):
761 def parents(self):
762 parents_recursion_limit = 5
762 parents_recursion_limit = 5
763 groups = []
763 groups = []
764 if self.parent_group is None:
764 if self.parent_group is None:
765 return groups
765 return groups
766 cur_gr = self.parent_group
766 cur_gr = self.parent_group
767 groups.insert(0, cur_gr)
767 groups.insert(0, cur_gr)
768 cnt = 0
768 cnt = 0
769 while 1:
769 while 1:
770 cnt += 1
770 cnt += 1
771 gr = getattr(cur_gr, 'parent_group', None)
771 gr = getattr(cur_gr, 'parent_group', None)
772 cur_gr = cur_gr.parent_group
772 cur_gr = cur_gr.parent_group
773 if gr is None:
773 if gr is None:
774 break
774 break
775 if cnt == parents_recursion_limit:
775 if cnt == parents_recursion_limit:
776 # this will prevent accidental infinit loops
776 # this will prevent accidental infinit loops
777 log.error('group nested more than %s' %
777 log.error('group nested more than %s' %
778 parents_recursion_limit)
778 parents_recursion_limit)
779 break
779 break
780
780
781 groups.insert(0, gr)
781 groups.insert(0, gr)
782 return groups
782 return groups
783
783
784 @property
784 @property
785 def children(self):
785 def children(self):
786 return RepoGroup.query().filter(RepoGroup.parent_group == self)
786 return RepoGroup.query().filter(RepoGroup.parent_group == self)
787
787
788 @property
788 @property
789 def name(self):
789 def name(self):
790 return self.group_name.split(RepoGroup.url_sep())[-1]
790 return self.group_name.split(RepoGroup.url_sep())[-1]
791
791
792 @property
792 @property
793 def full_path(self):
793 def full_path(self):
794 return self.group_name
794 return self.group_name
795
795
796 @property
796 @property
797 def full_path_splitted(self):
797 def full_path_splitted(self):
798 return self.group_name.split(RepoGroup.url_sep())
798 return self.group_name.split(RepoGroup.url_sep())
799
799
800 @property
800 @property
801 def repositories(self):
801 def repositories(self):
802 return Repository.query().filter(Repository.group == self)
802 return Repository.query().filter(Repository.group == self)
803
803
804 @property
804 @property
805 def repositories_recursive_count(self):
805 def repositories_recursive_count(self):
806 cnt = self.repositories.count()
806 cnt = self.repositories.count()
807
807
808 def children_count(group):
808 def children_count(group):
809 cnt = 0
809 cnt = 0
810 for child in group.children:
810 for child in group.children:
811 cnt += child.repositories.count()
811 cnt += child.repositories.count()
812 cnt += children_count(child)
812 cnt += children_count(child)
813 return cnt
813 return cnt
814
814
815 return cnt + children_count(self)
815 return cnt + children_count(self)
816
816
817 def get_new_name(self, group_name):
817 def get_new_name(self, group_name):
818 """
818 """
819 returns new full group name based on parent and new name
819 returns new full group name based on parent and new name
820
820
821 :param group_name:
821 :param group_name:
822 """
822 """
823 path_prefix = (self.parent_group.full_path_splitted if
823 path_prefix = (self.parent_group.full_path_splitted if
824 self.parent_group else [])
824 self.parent_group else [])
825 return RepoGroup.url_sep().join(path_prefix + [group_name])
825 return RepoGroup.url_sep().join(path_prefix + [group_name])
826
826
827
827
828 class Permission(Base, BaseModel):
828 class Permission(Base, BaseModel):
829 __tablename__ = 'permissions'
829 __tablename__ = 'permissions'
830 __table_args__ = {'extend_existing': True}
830 __table_args__ = {'extend_existing': True}
831 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
831 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
832 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
832 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
833 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
833 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
834
834
835 def __repr__(self):
835 def __repr__(self):
836 return "<%s('%s:%s')>" % (self.__class__.__name__,
836 return "<%s('%s:%s')>" % (self.__class__.__name__,
837 self.permission_id, self.permission_name)
837 self.permission_id, self.permission_name)
838
838
839 @classmethod
839 @classmethod
840 def get_by_key(cls, key):
840 def get_by_key(cls, key):
841 return cls.query().filter(cls.permission_name == key).scalar()
841 return cls.query().filter(cls.permission_name == key).scalar()
842
842
843 @classmethod
843 @classmethod
844 def get_default_perms(cls, default_user_id):
844 def get_default_perms(cls, default_user_id):
845 q = Session.query(UserRepoToPerm, Repository, cls)\
845 q = Session.query(UserRepoToPerm, Repository, cls)\
846 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
846 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
847 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
847 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
848 .filter(UserRepoToPerm.user_id == default_user_id)
848 .filter(UserRepoToPerm.user_id == default_user_id)
849
849
850 return q.all()
850 return q.all()
851
851
852
852
853 class UserRepoToPerm(Base, BaseModel):
853 class UserRepoToPerm(Base, BaseModel):
854 __tablename__ = 'repo_to_perm'
854 __tablename__ = 'repo_to_perm'
855 __table_args__ = (
855 __table_args__ = (
856 UniqueConstraint('user_id', 'repository_id'), {'extend_existing': True}
856 UniqueConstraint('user_id', 'repository_id'), {'extend_existing': True}
857 )
857 )
858 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
858 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
859 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
859 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
860 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
860 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
861 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
861 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
862
862
863 user = relationship('User')
863 user = relationship('User')
864 permission = relationship('Permission')
864 permission = relationship('Permission')
865 repository = relationship('Repository')
865 repository = relationship('Repository')
866
866
867 @classmethod
867 @classmethod
868 def create(cls, user, repository, permission):
868 def create(cls, user, repository, permission):
869 n = cls()
869 n = cls()
870 n.user = user
870 n.user = user
871 n.repository = repository
871 n.repository = repository
872 n.permission = permission
872 n.permission = permission
873 Session.add(n)
873 Session.add(n)
874 return n
874 return n
875
875
876 def __repr__(self):
876 def __repr__(self):
877 return '<user:%s => %s >' % (self.user, self.repository)
877 return '<user:%s => %s >' % (self.user, self.repository)
878
878
879
879
880 class UserToPerm(Base, BaseModel):
880 class UserToPerm(Base, BaseModel):
881 __tablename__ = 'user_to_perm'
881 __tablename__ = 'user_to_perm'
882 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing': True})
882 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing': True})
883 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
883 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
884 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
884 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
885 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
885 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
886
886
887 user = relationship('User')
887 user = relationship('User')
888 permission = relationship('Permission', lazy='joined')
888 permission = relationship('Permission', lazy='joined')
889
889
890
890
891 class UsersGroupRepoToPerm(Base, BaseModel):
891 class UsersGroupRepoToPerm(Base, BaseModel):
892 __tablename__ = 'users_group_repo_to_perm'
892 __tablename__ = 'users_group_repo_to_perm'
893 __table_args__ = (
893 __table_args__ = (
894 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
894 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
895 {'extend_existing': True}
895 {'extend_existing': True}
896 )
896 )
897 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
897 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
898 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
898 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
899 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
899 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
900 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
900 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
901
901
902 users_group = relationship('UsersGroup')
902 users_group = relationship('UsersGroup')
903 permission = relationship('Permission')
903 permission = relationship('Permission')
904 repository = relationship('Repository')
904 repository = relationship('Repository')
905
905
906 @classmethod
906 @classmethod
907 def create(cls, users_group, repository, permission):
907 def create(cls, users_group, repository, permission):
908 n = cls()
908 n = cls()
909 n.users_group = users_group
909 n.users_group = users_group
910 n.repository = repository
910 n.repository = repository
911 n.permission = permission
911 n.permission = permission
912 Session.add(n)
912 Session.add(n)
913 return n
913 return n
914
914
915 def __repr__(self):
915 def __repr__(self):
916 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
916 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
917
917
918
918
919 class UsersGroupToPerm(Base, BaseModel):
919 class UsersGroupToPerm(Base, BaseModel):
920 __tablename__ = 'users_group_to_perm'
920 __tablename__ = 'users_group_to_perm'
921 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
921 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
922 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
922 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
923 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
923 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
924
924
925 users_group = relationship('UsersGroup')
925 users_group = relationship('UsersGroup')
926 permission = relationship('Permission')
926 permission = relationship('Permission')
927
927
928
928
929 class UserRepoGroupToPerm(Base, BaseModel):
929 class UserRepoGroupToPerm(Base, BaseModel):
930 __tablename__ = 'user_repo_group_to_perm'
930 __tablename__ = 'user_repo_group_to_perm'
931 __table_args__ = (
931 __table_args__ = (
932 UniqueConstraint('group_id', 'permission_id'),
932 UniqueConstraint('group_id', 'permission_id'),
933 {'extend_existing': True}
933 {'extend_existing': True}
934 )
934 )
935
935
936 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
936 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
937 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
937 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
938 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
938 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
939 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
939 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
940
940
941 user = relationship('User')
941 user = relationship('User')
942 permission = relationship('Permission')
942 permission = relationship('Permission')
943 group = relationship('RepoGroup')
943 group = relationship('RepoGroup')
944
944
945
945
946 class UsersGroupRepoGroupToPerm(Base, BaseModel):
946 class UsersGroupRepoGroupToPerm(Base, BaseModel):
947 __tablename__ = 'users_group_repo_group_to_perm'
947 __tablename__ = 'users_group_repo_group_to_perm'
948 __table_args__ = (
948 __table_args__ = (
949 UniqueConstraint('group_id', 'permission_id'),
949 UniqueConstraint('group_id', 'permission_id'),
950 {'extend_existing': True}
950 {'extend_existing': True}
951 )
951 )
952
952
953 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
953 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
954 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
954 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
955 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
955 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
956 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
956 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
957
957
958 users_group = relationship('UsersGroup')
958 users_group = relationship('UsersGroup')
959 permission = relationship('Permission')
959 permission = relationship('Permission')
960 group = relationship('RepoGroup')
960 group = relationship('RepoGroup')
961
961
962
962
963 class Statistics(Base, BaseModel):
963 class Statistics(Base, BaseModel):
964 __tablename__ = 'statistics'
964 __tablename__ = 'statistics'
965 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing': True})
965 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing': True})
966 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
966 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
967 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
967 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
968 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
968 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
969 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
969 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
970 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
970 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
971 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
971 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
972
972
973 repository = relationship('Repository', single_parent=True)
973 repository = relationship('Repository', single_parent=True)
974
974
975
975
976 class UserFollowing(Base, BaseModel):
976 class UserFollowing(Base, BaseModel):
977 __tablename__ = 'user_followings'
977 __tablename__ = 'user_followings'
978 __table_args__ = (
978 __table_args__ = (
979 UniqueConstraint('user_id', 'follows_repository_id'),
979 UniqueConstraint('user_id', 'follows_repository_id'),
980 UniqueConstraint('user_id', 'follows_user_id'),
980 UniqueConstraint('user_id', 'follows_user_id'),
981 {'extend_existing': True}
981 {'extend_existing': True}
982 )
982 )
983
983
984 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
984 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
985 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
985 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
986 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
986 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
987 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
987 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
988 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
988 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
989
989
990 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
990 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
991
991
992 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
992 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
993 follows_repository = relationship('Repository', order_by='Repository.repo_name')
993 follows_repository = relationship('Repository', order_by='Repository.repo_name')
994
994
995 @classmethod
995 @classmethod
996 def get_repo_followers(cls, repo_id):
996 def get_repo_followers(cls, repo_id):
997 return cls.query().filter(cls.follows_repo_id == repo_id)
997 return cls.query().filter(cls.follows_repo_id == repo_id)
998
998
999
999
1000 class CacheInvalidation(Base, BaseModel):
1000 class CacheInvalidation(Base, BaseModel):
1001 __tablename__ = 'cache_invalidation'
1001 __tablename__ = 'cache_invalidation'
1002 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing': True})
1002 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing': True})
1003 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1003 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1004 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1004 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1005 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1005 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1006 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1006 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1007
1007
1008 def __init__(self, cache_key, cache_args=''):
1008 def __init__(self, cache_key, cache_args=''):
1009 self.cache_key = cache_key
1009 self.cache_key = cache_key
1010 self.cache_args = cache_args
1010 self.cache_args = cache_args
1011 self.cache_active = False
1011 self.cache_active = False
1012
1012
1013 def __repr__(self):
1013 def __repr__(self):
1014 return "<%s('%s:%s')>" % (self.__class__.__name__,
1014 return "<%s('%s:%s')>" % (self.__class__.__name__,
1015 self.cache_id, self.cache_key)
1015 self.cache_id, self.cache_key)
1016
1016
1017 @classmethod
1017 @classmethod
1018 def invalidate(cls, key):
1018 def invalidate(cls, key):
1019 """
1019 """
1020 Returns Invalidation object if this given key should be invalidated
1020 Returns Invalidation object if this given key should be invalidated
1021 None otherwise. `cache_active = False` means that this cache
1021 None otherwise. `cache_active = False` means that this cache
1022 state is not valid and needs to be invalidated
1022 state is not valid and needs to be invalidated
1023
1023
1024 :param key:
1024 :param key:
1025 """
1025 """
1026 return cls.query()\
1026 return cls.query()\
1027 .filter(CacheInvalidation.cache_key == key)\
1027 .filter(CacheInvalidation.cache_key == key)\
1028 .filter(CacheInvalidation.cache_active == False)\
1028 .filter(CacheInvalidation.cache_active == False)\
1029 .scalar()
1029 .scalar()
1030
1030
1031 @classmethod
1031 @classmethod
1032 def set_invalidate(cls, key):
1032 def set_invalidate(cls, key):
1033 """
1033 """
1034 Mark this Cache key for invalidation
1034 Mark this Cache key for invalidation
1035
1035
1036 :param key:
1036 :param key:
1037 """
1037 """
1038
1038
1039 log.debug('marking %s for invalidation' % key)
1039 log.debug('marking %s for invalidation' % key)
1040 inv_obj = Session.query(cls)\
1040 inv_obj = Session.query(cls)\
1041 .filter(cls.cache_key == key).scalar()
1041 .filter(cls.cache_key == key).scalar()
1042 if inv_obj:
1042 if inv_obj:
1043 inv_obj.cache_active = False
1043 inv_obj.cache_active = False
1044 else:
1044 else:
1045 log.debug('cache key not found in invalidation db -> creating one')
1045 log.debug('cache key not found in invalidation db -> creating one')
1046 inv_obj = CacheInvalidation(key)
1046 inv_obj = CacheInvalidation(key)
1047
1047
1048 try:
1048 try:
1049 Session.add(inv_obj)
1049 Session.add(inv_obj)
1050 Session.commit()
1050 Session.commit()
1051 except Exception:
1051 except Exception:
1052 log.error(traceback.format_exc())
1052 log.error(traceback.format_exc())
1053 Session.rollback()
1053 Session.rollback()
1054
1054
1055 @classmethod
1055 @classmethod
1056 def set_valid(cls, key):
1056 def set_valid(cls, key):
1057 """
1057 """
1058 Mark this cache key as active and currently cached
1058 Mark this cache key as active and currently cached
1059
1059
1060 :param key:
1060 :param key:
1061 """
1061 """
1062 inv_obj = CacheInvalidation.query()\
1062 inv_obj = CacheInvalidation.query()\
1063 .filter(CacheInvalidation.cache_key == key).scalar()
1063 .filter(CacheInvalidation.cache_key == key).scalar()
1064 inv_obj.cache_active = True
1064 inv_obj.cache_active = True
1065 Session.add(inv_obj)
1065 Session.add(inv_obj)
1066 Session.commit()
1066 Session.commit()
1067
1067
1068
1068
1069 class ChangesetComment(Base, BaseModel):
1069 class ChangesetComment(Base, BaseModel):
1070 __tablename__ = 'changeset_comments'
1070 __tablename__ = 'changeset_comments'
1071 __table_args__ = ({'extend_existing': True},)
1071 __table_args__ = ({'extend_existing': True},)
1072 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1072 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1073 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1073 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1074 revision = Column('revision', String(40), nullable=False)
1074 revision = Column('revision', String(40), nullable=False)
1075 line_no = Column('line_no', Unicode(10), nullable=True)
1075 line_no = Column('line_no', Unicode(10), nullable=True)
1076 f_path = Column('f_path', Unicode(1000), nullable=True)
1076 f_path = Column('f_path', Unicode(1000), nullable=True)
1077 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1077 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1078 text = Column('text', Unicode(25000), nullable=False)
1078 text = Column('text', Unicode(25000), nullable=False)
1079 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1079 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1080
1080
1081 author = relationship('User', lazy='joined')
1081 author = relationship('User', lazy='joined')
1082 repo = relationship('Repository')
1082 repo = relationship('Repository')
1083
1083
1084 @classmethod
1084 @classmethod
1085 def get_users(cls, revision):
1085 def get_users(cls, revision):
1086 """
1086 """
1087 Returns user associated with this changesetComment. ie those
1087 Returns user associated with this changesetComment. ie those
1088 who actually commented
1088 who actually commented
1089
1089
1090 :param cls:
1090 :param cls:
1091 :param revision:
1091 :param revision:
1092 """
1092 """
1093 return Session.query(User)\
1093 return Session.query(User)\
1094 .filter(cls.revision == revision)\
1094 .filter(cls.revision == revision)\
1095 .join(ChangesetComment.author).all()
1095 .join(ChangesetComment.author).all()
1096
1096
1097
1097
1098 class Notification(Base, BaseModel):
1098 class Notification(Base, BaseModel):
1099 __tablename__ = 'notifications'
1099 __tablename__ = 'notifications'
1100 __table_args__ = ({'extend_existing': True},)
1100 __table_args__ = ({'extend_existing': True},)
1101
1101
1102 TYPE_CHANGESET_COMMENT = u'cs_comment'
1102 TYPE_CHANGESET_COMMENT = u'cs_comment'
1103 TYPE_MESSAGE = u'message'
1103 TYPE_MESSAGE = u'message'
1104 TYPE_MENTION = u'mention'
1104 TYPE_MENTION = u'mention'
1105 TYPE_REGISTRATION = u'registration'
1105 TYPE_REGISTRATION = u'registration'
1106
1106
1107 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1107 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1108 subject = Column('subject', Unicode(512), nullable=True)
1108 subject = Column('subject', Unicode(512), nullable=True)
1109 body = Column('body', Unicode(50000), nullable=True)
1109 body = Column('body', Unicode(50000), nullable=True)
1110 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1110 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1111 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1111 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1112 type_ = Column('type', Unicode(256))
1112 type_ = Column('type', Unicode(256))
1113
1113
1114 created_by_user = relationship('User')
1114 created_by_user = relationship('User')
1115 notifications_to_users = relationship('UserNotification', lazy='joined',
1115 notifications_to_users = relationship('UserNotification', lazy='joined',
1116 cascade="all, delete, delete-orphan")
1116 cascade="all, delete, delete-orphan")
1117
1117
1118 @property
1118 @property
1119 def recipients(self):
1119 def recipients(self):
1120 return [x.user for x in UserNotification.query()\
1120 return [x.user for x in UserNotification.query()\
1121 .filter(UserNotification.notification == self).all()]
1121 .filter(UserNotification.notification == self).all()]
1122
1122
1123 @classmethod
1123 @classmethod
1124 def create(cls, created_by, subject, body, recipients, type_=None):
1124 def create(cls, created_by, subject, body, recipients, type_=None):
1125 if type_ is None:
1125 if type_ is None:
1126 type_ = Notification.TYPE_MESSAGE
1126 type_ = Notification.TYPE_MESSAGE
1127
1127
1128 notification = cls()
1128 notification = cls()
1129 notification.created_by_user = created_by
1129 notification.created_by_user = created_by
1130 notification.subject = subject
1130 notification.subject = subject
1131 notification.body = body
1131 notification.body = body
1132 notification.type_ = type_
1132 notification.type_ = type_
1133 notification.created_on = datetime.datetime.now()
1133 notification.created_on = datetime.datetime.now()
1134
1134
1135 for u in recipients:
1135 for u in recipients:
1136 assoc = UserNotification()
1136 assoc = UserNotification()
1137 assoc.notification = notification
1137 assoc.notification = notification
1138 u.notifications.append(assoc)
1138 u.notifications.append(assoc)
1139 Session.add(notification)
1139 Session.add(notification)
1140 return notification
1140 return notification
1141
1141
1142 @property
1142 @property
1143 def description(self):
1143 def description(self):
1144 from rhodecode.model.notification import NotificationModel
1144 from rhodecode.model.notification import NotificationModel
1145 return NotificationModel().make_description(self)
1145 return NotificationModel().make_description(self)
1146
1146
1147
1147
1148 class UserNotification(Base, BaseModel):
1148 class UserNotification(Base, BaseModel):
1149 __tablename__ = 'user_to_notification'
1149 __tablename__ = 'user_to_notification'
1150 __table_args__ = (
1150 __table_args__ = (
1151 UniqueConstraint('user_id', 'notification_id'),
1151 UniqueConstraint('user_id', 'notification_id'),
1152 {'extend_existing': True}
1152 {'extend_existing': True}
1153 )
1153 )
1154 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1154 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1155 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1155 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1156 read = Column('read', Boolean, default=False)
1156 read = Column('read', Boolean, default=False)
1157 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1157 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1158
1158
1159 user = relationship('User', lazy="joined")
1159 user = relationship('User', lazy="joined")
1160 notification = relationship('Notification', lazy="joined",
1160 notification = relationship('Notification', lazy="joined",
1161 order_by=lambda: Notification.created_on.desc(),)
1161 order_by=lambda: Notification.created_on.desc(),)
1162
1162
1163 def mark_as_read(self):
1163 def mark_as_read(self):
1164 self.read = True
1164 self.read = True
1165 Session.add(self)
1165 Session.add(self)
1166
1166
1167
1167
1168 class DbMigrateVersion(Base, BaseModel):
1168 class DbMigrateVersion(Base, BaseModel):
1169 __tablename__ = 'db_migrate_version'
1169 __tablename__ = 'db_migrate_version'
1170 __table_args__ = {'extend_existing': True}
1170 __table_args__ = {'extend_existing': True}
1171 repository_id = Column('repository_id', String(250), primary_key=True)
1171 repository_id = Column('repository_id', String(250), primary_key=True)
1172 repository_path = Column('repository_path', Text)
1172 repository_path = Column('repository_path', Text)
1173 version = Column('version', Integer)
1173 version = Column('version', Integer)
@@ -1,749 +1,749 b''
1 """ this is forms validation classes
1 """ this is forms validation classes
2 http://formencode.org/module-formencode.validators.html
2 http://formencode.org/module-formencode.validators.html
3 for list off all availible validators
3 for list off all availible validators
4
4
5 we can create our own validators
5 we can create our own validators
6
6
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
8 pre_validators [] These validators will be applied before the schema
8 pre_validators [] These validators will be applied before the schema
9 chained_validators [] These validators will be applied after the schema
9 chained_validators [] These validators will be applied after the schema
10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
14
14
15
15
16 <name> = formencode.validators.<name of validator>
16 <name> = formencode.validators.<name of validator>
17 <name> must equal form name
17 <name> must equal form name
18 list=[1,2,3,4,5]
18 list=[1,2,3,4,5]
19 for SELECT use formencode.All(OneOf(list), Int())
19 for SELECT use formencode.All(OneOf(list), Int())
20
20
21 """
21 """
22 import os
22 import os
23 import re
23 import re
24 import logging
24 import logging
25 import traceback
25 import traceback
26
26
27 import formencode
27 import formencode
28 from formencode import All
28 from formencode import All
29 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
29 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
30 Email, Bool, StringBoolean, Set
30 Email, Bool, StringBoolean, Set
31
31
32 from pylons.i18n.translation import _
32 from pylons.i18n.translation import _
33 from webhelpers.pylonslib.secure_form import authentication_token
33 from webhelpers.pylonslib.secure_form import authentication_token
34
34
35 from rhodecode.config.routing import ADMIN_PREFIX
35 from rhodecode.config.routing import ADMIN_PREFIX
36 from rhodecode.lib.utils import repo_name_slug
36 from rhodecode.lib.utils import repo_name_slug
37 from rhodecode.lib.auth import authenticate, get_crypt_password
37 from rhodecode.lib.auth import authenticate, get_crypt_password
38 from rhodecode.lib.exceptions import LdapImportError
38 from rhodecode.lib.exceptions import LdapImportError
39 from rhodecode.model.db import User, UsersGroup, RepoGroup, Repository
39 from rhodecode.model.db import User, UsersGroup, RepoGroup, Repository
40 from rhodecode import BACKENDS
40 from rhodecode import BACKENDS
41
41
42 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
43
43
44
44
45 #this is needed to translate the messages using _() in validators
45 #this is needed to translate the messages using _() in validators
46 class State_obj(object):
46 class State_obj(object):
47 _ = staticmethod(_)
47 _ = staticmethod(_)
48
48
49
49
50 #==============================================================================
50 #==============================================================================
51 # VALIDATORS
51 # VALIDATORS
52 #==============================================================================
52 #==============================================================================
53 class ValidAuthToken(formencode.validators.FancyValidator):
53 class ValidAuthToken(formencode.validators.FancyValidator):
54 messages = {'invalid_token': _('Token mismatch')}
54 messages = {'invalid_token': _('Token mismatch')}
55
55
56 def validate_python(self, value, state):
56 def validate_python(self, value, state):
57
57
58 if value != authentication_token():
58 if value != authentication_token():
59 raise formencode.Invalid(
59 raise formencode.Invalid(
60 self.message('invalid_token',
60 self.message('invalid_token',
61 state, search_number=value),
61 state, search_number=value),
62 value,
62 value,
63 state
63 state
64 )
64 )
65
65
66
66
67 def ValidUsername(edit, old_data):
67 def ValidUsername(edit, old_data):
68 class _ValidUsername(formencode.validators.FancyValidator):
68 class _ValidUsername(formencode.validators.FancyValidator):
69
69
70 def validate_python(self, value, state):
70 def validate_python(self, value, state):
71 if value in ['default', 'new_user']:
71 if value in ['default', 'new_user']:
72 raise formencode.Invalid(_('Invalid username'), value, state)
72 raise formencode.Invalid(_('Invalid username'), value, state)
73 #check if user is unique
73 #check if user is unique
74 old_un = None
74 old_un = None
75 if edit:
75 if edit:
76 old_un = User.get(old_data.get('user_id')).username
76 old_un = User.get(old_data.get('user_id')).username
77
77
78 if old_un != value or not edit:
78 if old_un != value or not edit:
79 if User.get_by_username(value, case_insensitive=True):
79 if User.get_by_username(value, case_insensitive=True):
80 raise formencode.Invalid(_('This username already '
80 raise formencode.Invalid(_('This username already '
81 'exists') , value, state)
81 'exists') , value, state)
82
82
83 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
83 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
84 raise formencode.Invalid(
84 raise formencode.Invalid(
85 _('Username may only contain alphanumeric characters '
85 _('Username may only contain alphanumeric characters '
86 'underscores, periods or dashes and must begin with '
86 'underscores, periods or dashes and must begin with '
87 'alphanumeric character'),
87 'alphanumeric character'),
88 value,
88 value,
89 state
89 state
90 )
90 )
91
91
92 return _ValidUsername
92 return _ValidUsername
93
93
94
94
95 def ValidUsersGroup(edit, old_data):
95 def ValidUsersGroup(edit, old_data):
96
96
97 class _ValidUsersGroup(formencode.validators.FancyValidator):
97 class _ValidUsersGroup(formencode.validators.FancyValidator):
98
98
99 def validate_python(self, value, state):
99 def validate_python(self, value, state):
100 if value in ['default']:
100 if value in ['default']:
101 raise formencode.Invalid(_('Invalid group name'), value, state)
101 raise formencode.Invalid(_('Invalid group name'), value, state)
102 #check if group is unique
102 #check if group is unique
103 old_ugname = None
103 old_ugname = None
104 if edit:
104 if edit:
105 old_ugname = UsersGroup.get(
105 old_ugname = UsersGroup.get(
106 old_data.get('users_group_id')).users_group_name
106 old_data.get('users_group_id')).users_group_name
107
107
108 if old_ugname != value or not edit:
108 if old_ugname != value or not edit:
109 if UsersGroup.get_by_group_name(value, cache=False,
109 if UsersGroup.get_by_group_name(value, cache=False,
110 case_insensitive=True):
110 case_insensitive=True):
111 raise formencode.Invalid(_('This users group '
111 raise formencode.Invalid(_('This users group '
112 'already exists'), value,
112 'already exists'), value,
113 state)
113 state)
114
114
115 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
115 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
116 raise formencode.Invalid(
116 raise formencode.Invalid(
117 _('RepoGroup name may only contain alphanumeric characters '
117 _('RepoGroup name may only contain alphanumeric characters '
118 'underscores, periods or dashes and must begin with '
118 'underscores, periods or dashes and must begin with '
119 'alphanumeric character'),
119 'alphanumeric character'),
120 value,
120 value,
121 state
121 state
122 )
122 )
123
123
124 return _ValidUsersGroup
124 return _ValidUsersGroup
125
125
126
126
127 def ValidReposGroup(edit, old_data):
127 def ValidReposGroup(edit, old_data):
128 class _ValidReposGroup(formencode.validators.FancyValidator):
128 class _ValidReposGroup(formencode.validators.FancyValidator):
129
129
130 def validate_python(self, value, state):
130 def validate_python(self, value, state):
131 # TODO WRITE VALIDATIONS
131 # TODO WRITE VALIDATIONS
132 group_name = value.get('group_name')
132 group_name = value.get('group_name')
133 group_parent_id = value.get('group_parent_id')
133 group_parent_id = value.get('group_parent_id')
134
134
135 # slugify repo group just in case :)
135 # slugify repo group just in case :)
136 slug = repo_name_slug(group_name)
136 slug = repo_name_slug(group_name)
137
137
138 # check for parent of self
138 # check for parent of self
139 parent_of_self = lambda: (
139 parent_of_self = lambda: (
140 old_data['group_id'] == int(group_parent_id)
140 old_data['group_id'] == int(group_parent_id)
141 if group_parent_id else False
141 if group_parent_id else False
142 )
142 )
143 if edit and parent_of_self():
143 if edit and parent_of_self():
144 e_dict = {
144 e_dict = {
145 'group_parent_id': _('Cannot assign this group as parent')
145 'group_parent_id': _('Cannot assign this group as parent')
146 }
146 }
147 raise formencode.Invalid('', value, state,
147 raise formencode.Invalid('', value, state,
148 error_dict=e_dict)
148 error_dict=e_dict)
149
149
150 old_gname = None
150 old_gname = None
151 if edit:
151 if edit:
152 old_gname = RepoGroup.get(old_data.get('group_id')).group_name
152 old_gname = RepoGroup.get(old_data.get('group_id')).group_name
153
153
154 if old_gname != group_name or not edit:
154 if old_gname != group_name or not edit:
155
155
156 # check group
156 # check group
157 gr = RepoGroup.query()\
157 gr = RepoGroup.query()\
158 .filter(RepoGroup.group_name == slug)\
158 .filter(RepoGroup.group_name == slug)\
159 .filter(RepoGroup.group_parent_id == group_parent_id)\
159 .filter(RepoGroup.group_parent_id == group_parent_id)\
160 .scalar()
160 .scalar()
161
161
162 if gr:
162 if gr:
163 e_dict = {
163 e_dict = {
164 'group_name': _('This group already exists')
164 'group_name': _('This group already exists')
165 }
165 }
166 raise formencode.Invalid('', value, state,
166 raise formencode.Invalid('', value, state,
167 error_dict=e_dict)
167 error_dict=e_dict)
168
168
169 # check for same repo
169 # check for same repo
170 repo = Repository.query()\
170 repo = Repository.query()\
171 .filter(Repository.repo_name == slug)\
171 .filter(Repository.repo_name == slug)\
172 .scalar()
172 .scalar()
173
173
174 if repo:
174 if repo:
175 e_dict = {
175 e_dict = {
176 'group_name': _('Repository with this name already exists')
176 'group_name': _('Repository with this name already exists')
177 }
177 }
178 raise formencode.Invalid('', value, state,
178 raise formencode.Invalid('', value, state,
179 error_dict=e_dict)
179 error_dict=e_dict)
180
180
181 return _ValidReposGroup
181 return _ValidReposGroup
182
182
183
183
184 class ValidPassword(formencode.validators.FancyValidator):
184 class ValidPassword(formencode.validators.FancyValidator):
185
185
186 def to_python(self, value, state):
186 def to_python(self, value, state):
187
187
188 if not value:
188 if not value:
189 return
189 return
190
190
191 if value.get('password'):
191 if value.get('password'):
192 try:
192 try:
193 value['password'] = get_crypt_password(value['password'])
193 value['password'] = get_crypt_password(value['password'])
194 except UnicodeEncodeError:
194 except UnicodeEncodeError:
195 e_dict = {'password': _('Invalid characters in password')}
195 e_dict = {'password': _('Invalid characters in password')}
196 raise formencode.Invalid('', value, state, error_dict=e_dict)
196 raise formencode.Invalid('', value, state, error_dict=e_dict)
197
197
198 if value.get('password_confirmation'):
198 if value.get('password_confirmation'):
199 try:
199 try:
200 value['password_confirmation'] = \
200 value['password_confirmation'] = \
201 get_crypt_password(value['password_confirmation'])
201 get_crypt_password(value['password_confirmation'])
202 except UnicodeEncodeError:
202 except UnicodeEncodeError:
203 e_dict = {
203 e_dict = {
204 'password_confirmation': _('Invalid characters in password')
204 'password_confirmation': _('Invalid characters in password')
205 }
205 }
206 raise formencode.Invalid('', value, state, error_dict=e_dict)
206 raise formencode.Invalid('', value, state, error_dict=e_dict)
207
207
208 if value.get('new_password'):
208 if value.get('new_password'):
209 try:
209 try:
210 value['new_password'] = \
210 value['new_password'] = \
211 get_crypt_password(value['new_password'])
211 get_crypt_password(value['new_password'])
212 except UnicodeEncodeError:
212 except UnicodeEncodeError:
213 e_dict = {'new_password': _('Invalid characters in password')}
213 e_dict = {'new_password': _('Invalid characters in password')}
214 raise formencode.Invalid('', value, state, error_dict=e_dict)
214 raise formencode.Invalid('', value, state, error_dict=e_dict)
215
215
216 return value
216 return value
217
217
218
218
219 class ValidPasswordsMatch(formencode.validators.FancyValidator):
219 class ValidPasswordsMatch(formencode.validators.FancyValidator):
220
220
221 def validate_python(self, value, state):
221 def validate_python(self, value, state):
222
222
223 pass_val = value.get('password') or value.get('new_password')
223 pass_val = value.get('password') or value.get('new_password')
224 if pass_val != value['password_confirmation']:
224 if pass_val != value['password_confirmation']:
225 e_dict = {'password_confirmation':
225 e_dict = {'password_confirmation':
226 _('Passwords do not match')}
226 _('Passwords do not match')}
227 raise formencode.Invalid('', value, state, error_dict=e_dict)
227 raise formencode.Invalid('', value, state, error_dict=e_dict)
228
228
229
229
230 class ValidAuth(formencode.validators.FancyValidator):
230 class ValidAuth(formencode.validators.FancyValidator):
231 messages = {
231 messages = {
232 'invalid_password':_('invalid password'),
232 'invalid_password':_('invalid password'),
233 'invalid_login':_('invalid user name'),
233 'invalid_login':_('invalid user name'),
234 'disabled_account':_('Your account is disabled')
234 'disabled_account':_('Your account is disabled')
235 }
235 }
236
236
237 # error mapping
237 # error mapping
238 e_dict = {'username': messages['invalid_login'],
238 e_dict = {'username': messages['invalid_login'],
239 'password': messages['invalid_password']}
239 'password': messages['invalid_password']}
240 e_dict_disable = {'username': messages['disabled_account']}
240 e_dict_disable = {'username': messages['disabled_account']}
241
241
242 def validate_python(self, value, state):
242 def validate_python(self, value, state):
243 password = value['password']
243 password = value['password']
244 username = value['username']
244 username = value['username']
245 user = User.get_by_username(username)
245 user = User.get_by_username(username)
246
246
247 if authenticate(username, password):
247 if authenticate(username, password):
248 return value
248 return value
249 else:
249 else:
250 if user and user.active is False:
250 if user and user.active is False:
251 log.warning('user %s is disabled', username)
251 log.warning('user %s is disabled', username)
252 raise formencode.Invalid(
252 raise formencode.Invalid(
253 self.message('disabled_account',
253 self.message('disabled_account',
254 state=State_obj),
254 state=State_obj),
255 value, state,
255 value, state,
256 error_dict=self.e_dict_disable
256 error_dict=self.e_dict_disable
257 )
257 )
258 else:
258 else:
259 log.warning('user %s not authenticated', username)
259 log.warning('user %s not authenticated', username)
260 raise formencode.Invalid(
260 raise formencode.Invalid(
261 self.message('invalid_password',
261 self.message('invalid_password',
262 state=State_obj), value, state,
262 state=State_obj), value, state,
263 error_dict=self.e_dict
263 error_dict=self.e_dict
264 )
264 )
265
265
266
266
267 class ValidRepoUser(formencode.validators.FancyValidator):
267 class ValidRepoUser(formencode.validators.FancyValidator):
268
268
269 def to_python(self, value, state):
269 def to_python(self, value, state):
270 try:
270 try:
271 User.query().filter(User.active == True)\
271 User.query().filter(User.active == True)\
272 .filter(User.username == value).one()
272 .filter(User.username == value).one()
273 except Exception:
273 except Exception:
274 raise formencode.Invalid(_('This username is not valid'),
274 raise formencode.Invalid(_('This username is not valid'),
275 value, state)
275 value, state)
276 return value
276 return value
277
277
278
278
279 def ValidRepoName(edit, old_data):
279 def ValidRepoName(edit, old_data):
280 class _ValidRepoName(formencode.validators.FancyValidator):
280 class _ValidRepoName(formencode.validators.FancyValidator):
281 def to_python(self, value, state):
281 def to_python(self, value, state):
282
282
283 repo_name = value.get('repo_name')
283 repo_name = value.get('repo_name')
284
284
285 slug = repo_name_slug(repo_name)
285 slug = repo_name_slug(repo_name)
286 if slug in [ADMIN_PREFIX, '']:
286 if slug in [ADMIN_PREFIX, '']:
287 e_dict = {'repo_name': _('This repository name is disallowed')}
287 e_dict = {'repo_name': _('This repository name is disallowed')}
288 raise formencode.Invalid('', value, state, error_dict=e_dict)
288 raise formencode.Invalid('', value, state, error_dict=e_dict)
289
289
290 if value.get('repo_group'):
290 if value.get('repo_group'):
291 gr = RepoGroup.get(value.get('repo_group'))
291 gr = RepoGroup.get(value.get('repo_group'))
292 group_path = gr.full_path
292 group_path = gr.full_path
293 # value needs to be aware of group name in order to check
293 # value needs to be aware of group name in order to check
294 # db key This is an actual just the name to store in the
294 # db key This is an actual just the name to store in the
295 # database
295 # database
296 repo_name_full = group_path + RepoGroup.url_sep() + repo_name
296 repo_name_full = group_path + RepoGroup.url_sep() + repo_name
297
297
298 else:
298 else:
299 group_path = ''
299 group_path = ''
300 repo_name_full = repo_name
300 repo_name_full = repo_name
301
301
302 value['repo_name_full'] = repo_name_full
302 value['repo_name_full'] = repo_name_full
303 rename = old_data.get('repo_name') != repo_name_full
303 rename = old_data.get('repo_name') != repo_name_full
304 create = not edit
304 create = not edit
305 if rename or create:
305 if rename or create:
306
306
307 if group_path != '':
307 if group_path != '':
308 if Repository.get_by_repo_name(repo_name_full):
308 if Repository.get_by_repo_name(repo_name_full):
309 e_dict = {
309 e_dict = {
310 'repo_name': _('This repository already exists in '
310 'repo_name': _('This repository already exists in '
311 'a group "%s"') % gr.group_name
311 'a group "%s"') % gr.group_name
312 }
312 }
313 raise formencode.Invalid('', value, state,
313 raise formencode.Invalid('', value, state,
314 error_dict=e_dict)
314 error_dict=e_dict)
315 elif RepoGroup.get_by_group_name(repo_name_full):
315 elif RepoGroup.get_by_group_name(repo_name_full):
316 e_dict = {
316 e_dict = {
317 'repo_name': _('There is a group with this name '
317 'repo_name': _('There is a group with this name '
318 'already "%s"') % repo_name_full
318 'already "%s"') % repo_name_full
319 }
319 }
320 raise formencode.Invalid('', value, state,
320 raise formencode.Invalid('', value, state,
321 error_dict=e_dict)
321 error_dict=e_dict)
322
322
323 elif Repository.get_by_repo_name(repo_name_full):
323 elif Repository.get_by_repo_name(repo_name_full):
324 e_dict = {'repo_name': _('This repository '
324 e_dict = {'repo_name': _('This repository '
325 'already exists')}
325 'already exists')}
326 raise formencode.Invalid('', value, state,
326 raise formencode.Invalid('', value, state,
327 error_dict=e_dict)
327 error_dict=e_dict)
328
328
329 return value
329 return value
330
330
331 return _ValidRepoName
331 return _ValidRepoName
332
332
333
333
334 def ValidForkName(*args, **kwargs):
334 def ValidForkName(*args, **kwargs):
335 return ValidRepoName(*args, **kwargs)
335 return ValidRepoName(*args, **kwargs)
336
336
337
337
338 def SlugifyName():
338 def SlugifyName():
339 class _SlugifyName(formencode.validators.FancyValidator):
339 class _SlugifyName(formencode.validators.FancyValidator):
340
340
341 def to_python(self, value, state):
341 def to_python(self, value, state):
342 return repo_name_slug(value)
342 return repo_name_slug(value)
343
343
344 return _SlugifyName
344 return _SlugifyName
345
345
346
346
347 def ValidCloneUri():
347 def ValidCloneUri():
348 from mercurial.httprepo import httprepository, httpsrepository
348 from mercurial.httprepo import httprepository, httpsrepository
349 from rhodecode.lib.utils import make_ui
349 from rhodecode.lib.utils import make_ui
350
350
351 class _ValidCloneUri(formencode.validators.FancyValidator):
351 class _ValidCloneUri(formencode.validators.FancyValidator):
352
352
353 def to_python(self, value, state):
353 def to_python(self, value, state):
354 if not value:
354 if not value:
355 pass
355 pass
356 elif value.startswith('https'):
356 elif value.startswith('https'):
357 try:
357 try:
358 httpsrepository(make_ui('db'), value).capabilities
358 httpsrepository(make_ui('db'), value).capabilities
359 except Exception:
359 except Exception:
360 log.error(traceback.format_exc())
360 log.error(traceback.format_exc())
361 raise formencode.Invalid(_('invalid clone url'), value,
361 raise formencode.Invalid(_('invalid clone url'), value,
362 state)
362 state)
363 elif value.startswith('http'):
363 elif value.startswith('http'):
364 try:
364 try:
365 httprepository(make_ui('db'), value).capabilities
365 httprepository(make_ui('db'), value).capabilities
366 except Exception:
366 except Exception:
367 log.error(traceback.format_exc())
367 log.error(traceback.format_exc())
368 raise formencode.Invalid(_('invalid clone url'), value,
368 raise formencode.Invalid(_('invalid clone url'), value,
369 state)
369 state)
370 else:
370 else:
371 raise formencode.Invalid(_('Invalid clone url, provide a '
371 raise formencode.Invalid(_('Invalid clone url, provide a '
372 'valid clone http\s url'), value,
372 'valid clone http\s url'), value,
373 state)
373 state)
374 return value
374 return value
375
375
376 return _ValidCloneUri
376 return _ValidCloneUri
377
377
378
378
379 def ValidForkType(old_data):
379 def ValidForkType(old_data):
380 class _ValidForkType(formencode.validators.FancyValidator):
380 class _ValidForkType(formencode.validators.FancyValidator):
381
381
382 def to_python(self, value, state):
382 def to_python(self, value, state):
383 if old_data['repo_type'] != value:
383 if old_data['repo_type'] != value:
384 raise formencode.Invalid(_('Fork have to be the same '
384 raise formencode.Invalid(_('Fork have to be the same '
385 'type as original'), value, state)
385 'type as original'), value, state)
386
386
387 return value
387 return value
388 return _ValidForkType
388 return _ValidForkType
389
389
390
390
391 class ValidPerms(formencode.validators.FancyValidator):
391 class ValidPerms(formencode.validators.FancyValidator):
392 messages = {'perm_new_member_name': _('This username or users group name'
392 messages = {'perm_new_member_name': _('This username or users group name'
393 ' is not valid')}
393 ' is not valid')}
394
394
395 def to_python(self, value, state):
395 def to_python(self, value, state):
396 perms_update = []
396 perms_update = []
397 perms_new = []
397 perms_new = []
398 #build a list of permission to update and new permission to create
398 #build a list of permission to update and new permission to create
399 for k, v in value.items():
399 for k, v in value.items():
400 #means new added member to permissions
400 #means new added member to permissions
401 if k.startswith('perm_new_member'):
401 if k.startswith('perm_new_member'):
402 new_perm = value.get('perm_new_member', False)
402 new_perm = value.get('perm_new_member', False)
403 new_member = value.get('perm_new_member_name', False)
403 new_member = value.get('perm_new_member_name', False)
404 new_type = value.get('perm_new_member_type')
404 new_type = value.get('perm_new_member_type')
405
405
406 if new_member and new_perm:
406 if new_member and new_perm:
407 if (new_member, new_perm, new_type) not in perms_new:
407 if (new_member, new_perm, new_type) not in perms_new:
408 perms_new.append((new_member, new_perm, new_type))
408 perms_new.append((new_member, new_perm, new_type))
409 elif k.startswith('u_perm_') or k.startswith('g_perm_'):
409 elif k.startswith('u_perm_') or k.startswith('g_perm_'):
410 member = k[7:]
410 member = k[7:]
411 t = {'u': 'user',
411 t = {'u': 'user',
412 'g': 'users_group'
412 'g': 'users_group'
413 }[k[0]]
413 }[k[0]]
414 if member == 'default':
414 if member == 'default':
415 if value['private']:
415 if value['private']:
416 #set none for default when updating to private repo
416 #set none for default when updating to private repo
417 v = 'repository.none'
417 v = 'repository.none'
418 perms_update.append((member, v, t))
418 perms_update.append((member, v, t))
419
419
420 value['perms_updates'] = perms_update
420 value['perms_updates'] = perms_update
421 value['perms_new'] = perms_new
421 value['perms_new'] = perms_new
422
422
423 #update permissions
423 #update permissions
424 for k, v, t in perms_new:
424 for k, v, t in perms_new:
425 try:
425 try:
426 if t is 'user':
426 if t is 'user':
427 self.user_db = User.query()\
427 self.user_db = User.query()\
428 .filter(User.active == True)\
428 .filter(User.active == True)\
429 .filter(User.username == k).one()
429 .filter(User.username == k).one()
430 if t is 'users_group':
430 if t is 'users_group':
431 self.user_db = UsersGroup.query()\
431 self.user_db = UsersGroup.query()\
432 .filter(UsersGroup.users_group_active == True)\
432 .filter(UsersGroup.users_group_active == True)\
433 .filter(UsersGroup.users_group_name == k).one()
433 .filter(UsersGroup.users_group_name == k).one()
434
434
435 except Exception:
435 except Exception:
436 msg = self.message('perm_new_member_name',
436 msg = self.message('perm_new_member_name',
437 state=State_obj)
437 state=State_obj)
438 raise formencode.Invalid(
438 raise formencode.Invalid(
439 msg, value, state, error_dict={'perm_new_member_name': msg}
439 msg, value, state, error_dict={'perm_new_member_name': msg}
440 )
440 )
441 return value
441 return value
442
442
443
443
444 class ValidSettings(formencode.validators.FancyValidator):
444 class ValidSettings(formencode.validators.FancyValidator):
445
445
446 def to_python(self, value, state):
446 def to_python(self, value, state):
447 # settings form can't edit user
447 # settings form can't edit user
448 if 'user' in value:
448 if 'user' in value:
449 del['value']['user']
449 del['value']['user']
450 return value
450 return value
451
451
452
452
453 class ValidPath(formencode.validators.FancyValidator):
453 class ValidPath(formencode.validators.FancyValidator):
454 def to_python(self, value, state):
454 def to_python(self, value, state):
455
455
456 if not os.path.isdir(value):
456 if not os.path.isdir(value):
457 msg = _('This is not a valid path')
457 msg = _('This is not a valid path')
458 raise formencode.Invalid(msg, value, state,
458 raise formencode.Invalid(msg, value, state,
459 error_dict={'paths_root_path': msg})
459 error_dict={'paths_root_path': msg})
460 return value
460 return value
461
461
462
462
463 def UniqSystemEmail(old_data):
463 def UniqSystemEmail(old_data):
464 class _UniqSystemEmail(formencode.validators.FancyValidator):
464 class _UniqSystemEmail(formencode.validators.FancyValidator):
465 def to_python(self, value, state):
465 def to_python(self, value, state):
466 value = value.lower()
466 value = value.lower()
467 if old_data.get('email', '').lower() != value:
467 if old_data.get('email', '').lower() != value:
468 user = User.get_by_email(value, case_insensitive=True)
468 user = User.get_by_email(value, case_insensitive=True)
469 if user:
469 if user:
470 raise formencode.Invalid(
470 raise formencode.Invalid(
471 _("This e-mail address is already taken"), value, state
471 _("This e-mail address is already taken"), value, state
472 )
472 )
473 return value
473 return value
474
474
475 return _UniqSystemEmail
475 return _UniqSystemEmail
476
476
477
477
478 class ValidSystemEmail(formencode.validators.FancyValidator):
478 class ValidSystemEmail(formencode.validators.FancyValidator):
479 def to_python(self, value, state):
479 def to_python(self, value, state):
480 value = value.lower()
480 value = value.lower()
481 user = User.get_by_email(value, case_insensitive=True)
481 user = User.get_by_email(value, case_insensitive=True)
482 if user is None:
482 if user is None:
483 raise formencode.Invalid(
483 raise formencode.Invalid(
484 _("This e-mail address doesn't exist."), value, state
484 _("This e-mail address doesn't exist."), value, state
485 )
485 )
486
486
487 return value
487 return value
488
488
489
489
490 class LdapLibValidator(formencode.validators.FancyValidator):
490 class LdapLibValidator(formencode.validators.FancyValidator):
491
491
492 def to_python(self, value, state):
492 def to_python(self, value, state):
493
493
494 try:
494 try:
495 import ldap
495 import ldap
496 except ImportError:
496 except ImportError:
497 raise LdapImportError
497 raise LdapImportError
498 return value
498 return value
499
499
500
500
501 class AttrLoginValidator(formencode.validators.FancyValidator):
501 class AttrLoginValidator(formencode.validators.FancyValidator):
502
502
503 def to_python(self, value, state):
503 def to_python(self, value, state):
504
504
505 if not value or not isinstance(value, (str, unicode)):
505 if not value or not isinstance(value, (str, unicode)):
506 raise formencode.Invalid(
506 raise formencode.Invalid(
507 _("The LDAP Login attribute of the CN must be specified - "
507 _("The LDAP Login attribute of the CN must be specified - "
508 "this is the name of the attribute that is equivalent "
508 "this is the name of the attribute that is equivalent "
509 "to 'username'"), value, state
509 "to 'username'"), value, state
510 )
510 )
511
511
512 return value
512 return value
513
513
514
514
515 #==============================================================================
515 #==============================================================================
516 # FORMS
516 # FORMS
517 #==============================================================================
517 #==============================================================================
518 class LoginForm(formencode.Schema):
518 class LoginForm(formencode.Schema):
519 allow_extra_fields = True
519 allow_extra_fields = True
520 filter_extra_fields = True
520 filter_extra_fields = True
521 username = UnicodeString(
521 username = UnicodeString(
522 strip=True,
522 strip=True,
523 min=1,
523 min=1,
524 not_empty=True,
524 not_empty=True,
525 messages={
525 messages={
526 'empty': _('Please enter a login'),
526 'empty': _('Please enter a login'),
527 'tooShort': _('Enter a value %(min)i characters long or more')}
527 'tooShort': _('Enter a value %(min)i characters long or more')}
528 )
528 )
529
529
530 password = UnicodeString(
530 password = UnicodeString(
531 strip=True,
531 strip=True,
532 min=3,
532 min=3,
533 not_empty=True,
533 not_empty=True,
534 messages={
534 messages={
535 'empty': _('Please enter a password'),
535 'empty': _('Please enter a password'),
536 'tooShort': _('Enter %(min)i characters or more')}
536 'tooShort': _('Enter %(min)i characters or more')}
537 )
537 )
538
538
539 remember = StringBoolean(if_missing=False)
539 remember = StringBoolean(if_missing=False)
540
540
541 chained_validators = [ValidAuth]
541 chained_validators = [ValidAuth]
542
542
543
543
544 def UserForm(edit=False, old_data={}):
544 def UserForm(edit=False, old_data={}):
545 class _UserForm(formencode.Schema):
545 class _UserForm(formencode.Schema):
546 allow_extra_fields = True
546 allow_extra_fields = True
547 filter_extra_fields = True
547 filter_extra_fields = True
548 username = All(UnicodeString(strip=True, min=1, not_empty=True),
548 username = All(UnicodeString(strip=True, min=1, not_empty=True),
549 ValidUsername(edit, old_data))
549 ValidUsername(edit, old_data))
550 if edit:
550 if edit:
551 new_password = All(UnicodeString(strip=True, min=6, not_empty=False))
551 new_password = All(UnicodeString(strip=True, min=6, not_empty=False))
552 password_confirmation = All(UnicodeString(strip=True, min=6,
552 password_confirmation = All(UnicodeString(strip=True, min=6,
553 not_empty=False))
553 not_empty=False))
554 admin = StringBoolean(if_missing=False)
554 admin = StringBoolean(if_missing=False)
555 else:
555 else:
556 password = All(UnicodeString(strip=True, min=6, not_empty=True))
556 password = All(UnicodeString(strip=True, min=6, not_empty=True))
557 password_confirmation = All(UnicodeString(strip=True, min=6,
557 password_confirmation = All(UnicodeString(strip=True, min=6,
558 not_empty=False))
558 not_empty=False))
559
559
560 active = StringBoolean(if_missing=False)
560 active = StringBoolean(if_missing=False)
561 name = UnicodeString(strip=True, min=1, not_empty=False)
561 name = UnicodeString(strip=True, min=1, not_empty=False)
562 lastname = UnicodeString(strip=True, min=1, not_empty=False)
562 lastname = UnicodeString(strip=True, min=1, not_empty=False)
563 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
563 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
564
564
565 chained_validators = [ValidPasswordsMatch, ValidPassword]
565 chained_validators = [ValidPasswordsMatch, ValidPassword]
566
566
567 return _UserForm
567 return _UserForm
568
568
569
569
570 def UsersGroupForm(edit=False, old_data={}, available_members=[]):
570 def UsersGroupForm(edit=False, old_data={}, available_members=[]):
571 class _UsersGroupForm(formencode.Schema):
571 class _UsersGroupForm(formencode.Schema):
572 allow_extra_fields = True
572 allow_extra_fields = True
573 filter_extra_fields = True
573 filter_extra_fields = True
574
574
575 users_group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
575 users_group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
576 ValidUsersGroup(edit, old_data))
576 ValidUsersGroup(edit, old_data))
577
577
578 users_group_active = StringBoolean(if_missing=False)
578 users_group_active = StringBoolean(if_missing=False)
579
579
580 if edit:
580 if edit:
581 users_group_members = OneOf(available_members, hideList=False,
581 users_group_members = OneOf(available_members, hideList=False,
582 testValueList=True,
582 testValueList=True,
583 if_missing=None, not_empty=False)
583 if_missing=None, not_empty=False)
584
584
585 return _UsersGroupForm
585 return _UsersGroupForm
586
586
587
587
588 def ReposGroupForm(edit=False, old_data={}, available_groups=[]):
588 def ReposGroupForm(edit=False, old_data={}, available_groups=[]):
589 class _ReposGroupForm(formencode.Schema):
589 class _ReposGroupForm(formencode.Schema):
590 allow_extra_fields = True
590 allow_extra_fields = True
591 filter_extra_fields = True
591 filter_extra_fields = True
592
592
593 group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
593 group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
594 SlugifyName())
594 SlugifyName())
595 group_description = UnicodeString(strip=True, min=1,
595 group_description = UnicodeString(strip=True, min=1,
596 not_empty=True)
596 not_empty=True)
597 group_parent_id = OneOf(available_groups, hideList=False,
597 group_parent_id = OneOf(available_groups, hideList=False,
598 testValueList=True,
598 testValueList=True,
599 if_missing=None, not_empty=False)
599 if_missing=None, not_empty=False)
600
600
601 chained_validators = [ValidReposGroup(edit, old_data)]
601 chained_validators = [ValidReposGroup(edit, old_data)]
602
602
603 return _ReposGroupForm
603 return _ReposGroupForm
604
604
605
605
606 def RegisterForm(edit=False, old_data={}):
606 def RegisterForm(edit=False, old_data={}):
607 class _RegisterForm(formencode.Schema):
607 class _RegisterForm(formencode.Schema):
608 allow_extra_fields = True
608 allow_extra_fields = True
609 filter_extra_fields = True
609 filter_extra_fields = True
610 username = All(ValidUsername(edit, old_data),
610 username = All(ValidUsername(edit, old_data),
611 UnicodeString(strip=True, min=1, not_empty=True))
611 UnicodeString(strip=True, min=1, not_empty=True))
612 password = All(UnicodeString(strip=True, min=6, not_empty=True))
612 password = All(UnicodeString(strip=True, min=6, not_empty=True))
613 password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=True))
613 password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=True))
614 active = StringBoolean(if_missing=False)
614 active = StringBoolean(if_missing=False)
615 name = UnicodeString(strip=True, min=1, not_empty=False)
615 name = UnicodeString(strip=True, min=1, not_empty=False)
616 lastname = UnicodeString(strip=True, min=1, not_empty=False)
616 lastname = UnicodeString(strip=True, min=1, not_empty=False)
617 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
617 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
618
618
619 chained_validators = [ValidPasswordsMatch, ValidPassword]
619 chained_validators = [ValidPasswordsMatch, ValidPassword]
620
620
621 return _RegisterForm
621 return _RegisterForm
622
622
623
623
624 def PasswordResetForm():
624 def PasswordResetForm():
625 class _PasswordResetForm(formencode.Schema):
625 class _PasswordResetForm(formencode.Schema):
626 allow_extra_fields = True
626 allow_extra_fields = True
627 filter_extra_fields = True
627 filter_extra_fields = True
628 email = All(ValidSystemEmail(), Email(not_empty=True))
628 email = All(ValidSystemEmail(), Email(not_empty=True))
629 return _PasswordResetForm
629 return _PasswordResetForm
630
630
631
631
632 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
632 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
633 repo_groups=[]):
633 repo_groups=[]):
634 class _RepoForm(formencode.Schema):
634 class _RepoForm(formencode.Schema):
635 allow_extra_fields = True
635 allow_extra_fields = True
636 filter_extra_fields = False
636 filter_extra_fields = False
637 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
637 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
638 SlugifyName())
638 SlugifyName())
639 clone_uri = All(UnicodeString(strip=True, min=1, not_empty=False),
639 clone_uri = All(UnicodeString(strip=True, min=1, not_empty=False),
640 ValidCloneUri()())
640 ValidCloneUri()())
641 repo_group = OneOf(repo_groups, hideList=True)
641 repo_group = OneOf(repo_groups, hideList=True)
642 repo_type = OneOf(supported_backends)
642 repo_type = OneOf(supported_backends)
643 description = UnicodeString(strip=True, min=1, not_empty=True)
643 description = UnicodeString(strip=True, min=1, not_empty=True)
644 private = StringBoolean(if_missing=False)
644 private = StringBoolean(if_missing=False)
645 enable_statistics = StringBoolean(if_missing=False)
645 enable_statistics = StringBoolean(if_missing=False)
646 enable_downloads = StringBoolean(if_missing=False)
646 enable_downloads = StringBoolean(if_missing=False)
647
647
648 if edit:
648 if edit:
649 #this is repo owner
649 #this is repo owner
650 user = All(UnicodeString(not_empty=True), ValidRepoUser)
650 user = All(UnicodeString(not_empty=True), ValidRepoUser)
651
651
652 chained_validators = [ValidRepoName(edit, old_data), ValidPerms]
652 chained_validators = [ValidRepoName(edit, old_data), ValidPerms]
653 return _RepoForm
653 return _RepoForm
654
654
655
655
656 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
656 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
657 repo_groups=[]):
657 repo_groups=[]):
658 class _RepoForkForm(formencode.Schema):
658 class _RepoForkForm(formencode.Schema):
659 allow_extra_fields = True
659 allow_extra_fields = True
660 filter_extra_fields = False
660 filter_extra_fields = False
661 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
661 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
662 SlugifyName())
662 SlugifyName())
663 repo_group = OneOf(repo_groups, hideList=True)
663 repo_group = OneOf(repo_groups, hideList=True)
664 repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
664 repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
665 description = UnicodeString(strip=True, min=1, not_empty=True)
665 description = UnicodeString(strip=True, min=1, not_empty=True)
666 private = StringBoolean(if_missing=False)
666 private = StringBoolean(if_missing=False)
667 copy_permissions = StringBoolean(if_missing=False)
667 copy_permissions = StringBoolean(if_missing=False)
668 update_after_clone = StringBoolean(if_missing=False)
668 update_after_clone = StringBoolean(if_missing=False)
669 fork_parent_id = UnicodeString()
669 fork_parent_id = UnicodeString()
670 chained_validators = [ValidForkName(edit, old_data)]
670 chained_validators = [ValidForkName(edit, old_data)]
671
671
672 return _RepoForkForm
672 return _RepoForkForm
673
673
674
674
675 def RepoSettingsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
675 def RepoSettingsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
676 repo_groups=[]):
676 repo_groups=[]):
677 class _RepoForm(formencode.Schema):
677 class _RepoForm(formencode.Schema):
678 allow_extra_fields = True
678 allow_extra_fields = True
679 filter_extra_fields = False
679 filter_extra_fields = False
680 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
680 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
681 SlugifyName())
681 SlugifyName())
682 description = UnicodeString(strip=True, min=1, not_empty=True)
682 description = UnicodeString(strip=True, min=1, not_empty=True)
683 repo_group = OneOf(repo_groups, hideList=True)
683 repo_group = OneOf(repo_groups, hideList=True)
684 private = StringBoolean(if_missing=False)
684 private = StringBoolean(if_missing=False)
685
685
686 chained_validators = [ValidRepoName(edit, old_data), ValidPerms,
686 chained_validators = [ValidRepoName(edit, old_data), ValidPerms,
687 ValidSettings]
687 ValidSettings]
688 return _RepoForm
688 return _RepoForm
689
689
690
690
691 def ApplicationSettingsForm():
691 def ApplicationSettingsForm():
692 class _ApplicationSettingsForm(formencode.Schema):
692 class _ApplicationSettingsForm(formencode.Schema):
693 allow_extra_fields = True
693 allow_extra_fields = True
694 filter_extra_fields = False
694 filter_extra_fields = False
695 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
695 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
696 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
696 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
697 rhodecode_ga_code = UnicodeString(strip=True, min=1, not_empty=False)
697 rhodecode_ga_code = UnicodeString(strip=True, min=1, not_empty=False)
698
698
699 return _ApplicationSettingsForm
699 return _ApplicationSettingsForm
700
700
701
701
702 def ApplicationUiSettingsForm():
702 def ApplicationUiSettingsForm():
703 class _ApplicationUiSettingsForm(formencode.Schema):
703 class _ApplicationUiSettingsForm(formencode.Schema):
704 allow_extra_fields = True
704 allow_extra_fields = True
705 filter_extra_fields = False
705 filter_extra_fields = False
706 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
706 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
707 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
707 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
708 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
708 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
709 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
709 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
710 hooks_pretxnchangegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
710 hooks_pretxnchangegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
711 hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
711 hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
712
712
713 return _ApplicationUiSettingsForm
713 return _ApplicationUiSettingsForm
714
714
715
715
716 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
716 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
717 class _DefaultPermissionsForm(formencode.Schema):
717 class _DefaultPermissionsForm(formencode.Schema):
718 allow_extra_fields = True
718 allow_extra_fields = True
719 filter_extra_fields = True
719 filter_extra_fields = True
720 overwrite_default = StringBoolean(if_missing=False)
720 overwrite_default = StringBoolean(if_missing=False)
721 anonymous = OneOf(['True', 'False'], if_missing=False)
721 anonymous = OneOf(['True', 'False'], if_missing=False)
722 default_perm = OneOf(perms_choices)
722 default_perm = OneOf(perms_choices)
723 default_register = OneOf(register_choices)
723 default_register = OneOf(register_choices)
724 default_create = OneOf(create_choices)
724 default_create = OneOf(create_choices)
725
725
726 return _DefaultPermissionsForm
726 return _DefaultPermissionsForm
727
727
728
728
729 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices, tls_kind_choices):
729 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices, tls_kind_choices):
730 class _LdapSettingsForm(formencode.Schema):
730 class _LdapSettingsForm(formencode.Schema):
731 allow_extra_fields = True
731 allow_extra_fields = True
732 filter_extra_fields = True
732 filter_extra_fields = True
733 pre_validators = [LdapLibValidator]
733 pre_validators = [LdapLibValidator]
734 ldap_active = StringBoolean(if_missing=False)
734 ldap_active = StringBoolean(if_missing=False)
735 ldap_host = UnicodeString(strip=True,)
735 ldap_host = UnicodeString(strip=True,)
736 ldap_port = Number(strip=True,)
736 ldap_port = Number(strip=True,)
737 ldap_tls_kind = OneOf(tls_kind_choices)
737 ldap_tls_kind = OneOf(tls_kind_choices)
738 ldap_tls_reqcert = OneOf(tls_reqcert_choices)
738 ldap_tls_reqcert = OneOf(tls_reqcert_choices)
739 ldap_dn_user = UnicodeString(strip=True,)
739 ldap_dn_user = UnicodeString(strip=True,)
740 ldap_dn_pass = UnicodeString(strip=True,)
740 ldap_dn_pass = UnicodeString(strip=True,)
741 ldap_base_dn = UnicodeString(strip=True,)
741 ldap_base_dn = UnicodeString(strip=True,)
742 ldap_filter = UnicodeString(strip=True,)
742 ldap_filter = UnicodeString(strip=True,)
743 ldap_search_scope = OneOf(search_scope_choices)
743 ldap_search_scope = OneOf(search_scope_choices)
744 ldap_attr_login = All(AttrLoginValidator, UnicodeString(strip=True,))
744 ldap_attr_login = All(AttrLoginValidator, UnicodeString(strip=True,))
745 ldap_attr_firstname = UnicodeString(strip=True,)
745 ldap_attr_firstname = UnicodeString(strip=True,)
746 ldap_attr_lastname = UnicodeString(strip=True,)
746 ldap_attr_lastname = UnicodeString(strip=True,)
747 ldap_attr_email = UnicodeString(strip=True,)
747 ldap_attr_email = UnicodeString(strip=True,)
748
748
749 return _LdapSettingsForm
749 return _LdapSettingsForm
@@ -1,192 +1,192 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} ${_('Changeset')} - r${c.changeset.revision}:${h.short_id(c.changeset.raw_id)} - ${c.rhodecode_name}
6 ${c.repo_name} ${_('Changeset')} - r${c.changeset.revision}:${h.short_id(c.changeset.raw_id)} - ${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 ${_('Changeset')} - <span class='hash'>r${c.changeset.revision}:${h.short_id(c.changeset.raw_id)}</span>
14 ${_('Changeset')} - <span class='hash'>r${c.changeset.revision}:${h.short_id(c.changeset.raw_id)}</span>
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 <div class="diffblock">
28 <div class="diffblock">
29 <div class="code-header">
29 <div class="code-header">
30 <div class="hash">
30 <div class="hash">
31 r${c.changeset.revision}:${h.short_id(c.changeset.raw_id)}
31 r${c.changeset.revision}:${h.short_id(c.changeset.raw_id)}
32 </div>
32 </div>
33 <div class="date">
33 <div class="date">
34 ${c.changeset.date}
34 ${c.changeset.date}
35 </div>
35 </div>
36 <div class="diff-actions">
36 <div class="diff-actions">
37 <a href="${h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='show')}" title="${_('raw diff')}" class="tooltip"><img class="icon" src="${h.url('/images/icons/page_white.png')}"/></a>
37 <a href="${h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='show')}" title="${_('raw diff')}" class="tooltip"><img class="icon" src="${h.url('/images/icons/page_white.png')}"/></a>
38 <a href="${h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='download')}" title="${_('download diff')}" class="tooltip"><img class="icon" src="${h.url('/images/icons/page_white_get.png')}"/></a>
38 <a href="${h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='download')}" title="${_('download diff')}" class="tooltip"><img class="icon" src="${h.url('/images/icons/page_white_get.png')}"/></a>
39 ${c.ignorews_url()}
39 ${c.ignorews_url()}
40 ${c.context_url()}
40 ${c.context_url()}
41 </div>
41 </div>
42 <div class="comments-number" style="float:right;padding-right:5px">${len(c.comments)} comment(s) (${c.inline_cnt} ${_('inline')})</div>
42 <div class="comments-number" style="float:right;padding-right:5px">${len(c.comments)} comment(s) (${c.inline_cnt} ${_('inline')})</div>
43 </div>
43 </div>
44 </div>
44 </div>
45 <div id="changeset_content">
45 <div id="changeset_content">
46 <div class="container">
46 <div class="container">
47 <div class="left">
47 <div class="left">
48 <div class="author">
48 <div class="author">
49 <div class="gravatar">
49 <div class="gravatar">
50 <img alt="gravatar" src="${h.gravatar_url(h.email(c.changeset.author),20)}"/>
50 <img alt="gravatar" src="${h.gravatar_url(h.email(c.changeset.author),20)}"/>
51 </div>
51 </div>
52 <span>${h.person(c.changeset.author)}</span><br/>
52 <span>${h.person(c.changeset.author)}</span><br/>
53 <span><a href="mailto:${h.email_or_none(c.changeset.author)}">${h.email_or_none(c.changeset.author)}</a></span><br/>
53 <span><a href="mailto:${h.email_or_none(c.changeset.author)}">${h.email_or_none(c.changeset.author)}</a></span><br/>
54 </div>
54 </div>
55 <div class="message">${h.urlify_commit(h.wrap_paragraphs(c.changeset.message),c.repo_name)}</div>
55 <div class="message">${h.urlify_commit(h.wrap_paragraphs(c.changeset.message),c.repo_name)}</div>
56 </div>
56 </div>
57 <div class="right">
57 <div class="right">
58 <div class="changes">
58 <div class="changes">
59 % if len(c.changeset.affected_files) <= c.affected_files_cut_off:
59 % if len(c.changeset.affected_files) <= c.affected_files_cut_off:
60 <span class="removed" title="${_('removed')}">${len(c.changeset.removed)}</span>
60 <span class="removed" title="${_('removed')}">${len(c.changeset.removed)}</span>
61 <span class="changed" title="${_('changed')}">${len(c.changeset.changed)}</span>
61 <span class="changed" title="${_('changed')}">${len(c.changeset.changed)}</span>
62 <span class="added" title="${_('added')}">${len(c.changeset.added)}</span>
62 <span class="added" title="${_('added')}">${len(c.changeset.added)}</span>
63 % else:
63 % else:
64 <span class="removed" title="${_('affected %s files') % len(c.changeset.affected_files)}">!</span>
64 <span class="removed" title="${_('affected %s files') % len(c.changeset.affected_files)}">!</span>
65 <span class="changed" title="${_('affected %s files') % len(c.changeset.affected_files)}">!</span>
65 <span class="changed" title="${_('affected %s files') % len(c.changeset.affected_files)}">!</span>
66 <span class="added" title="${_('affected %s files') % len(c.changeset.affected_files)}">!</span>
66 <span class="added" title="${_('affected %s files') % len(c.changeset.affected_files)}">!</span>
67 % endif
67 % endif
68 </div>
68 </div>
69
69
70 %if c.changeset.parents:
70 %if c.changeset.parents:
71 %for p_cs in reversed(c.changeset.parents):
71 %for p_cs in reversed(c.changeset.parents):
72 <div class="parent">${_('Parent')}
72 <div class="parent">${_('Parent')}
73 <span class="changeset_id">${p_cs.revision}:<span class="changeset_hash">${h.link_to(h.short_id(p_cs.raw_id),
73 <span class="changeset_id">${p_cs.revision}:<span class="changeset_hash">${h.link_to(h.short_id(p_cs.raw_id),
74 h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.raw_id),title=p_cs.message)}</span></span>
74 h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.raw_id),title=p_cs.message)}</span></span>
75 </div>
75 </div>
76 %endfor
76 %endfor
77 %else:
77 %else:
78 <div class="parent">${_('No parents')}</div>
78 <div class="parent">${_('No parents')}</div>
79 %endif
79 %endif
80 <span class="logtags">
80 <span class="logtags">
81 %if len(c.changeset.parents)>1:
81 %if len(c.changeset.parents)>1:
82 <span class="merge">${_('merge')}</span>
82 <span class="merge">${_('merge')}</span>
83 %endif
83 %endif
84 <span class="branchtag" title="${'%s %s' % (_('branch'),c.changeset.branch)}">
84 <span class="branchtag" title="${'%s %s' % (_('branch'),c.changeset.branch)}">
85 ${h.link_to(c.changeset.branch,h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id))}</span>
85 ${h.link_to(c.changeset.branch,h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id))}</span>
86 %for tag in c.changeset.tags:
86 %for tag in c.changeset.tags:
87 <span class="tagtag" title="${'%s %s' % (_('tag'),tag)}">
87 <span class="tagtag" title="${'%s %s' % (_('tag'),tag)}">
88 ${h.link_to(tag,h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id))}</span>
88 ${h.link_to(tag,h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id))}</span>
89 %endfor
89 %endfor
90 </span>
90 </span>
91 </div>
91 </div>
92 </div>
92 </div>
93 <span>
93 <span>
94 ${_('%s files affected with %s additions and %s deletions:') % (len(c.changeset.affected_files),c.lines_added,c.lines_deleted)}
94 ${_('%s files affected with %s additions and %s deletions:') % (len(c.changeset.affected_files),c.lines_added,c.lines_deleted)}
95 </span>
95 </span>
96 <div class="cs_files">
96 <div class="cs_files">
97 %for change,filenode,diff,cs1,cs2,stat in c.changes:
97 %for change,filenode,diff,cs1,cs2,stat in c.changes:
98 <div class="cs_${change}">
98 <div class="cs_${change}">
99 <div class="node">
99 <div class="node">
100 %if change != 'removed':
100 %if change != 'removed':
101 ${h.link_to(h.safe_unicode(filenode.path),c.anchor_url(filenode.changeset.raw_id,filenode.path)+"_target")}
101 ${h.link_to(h.safe_unicode(filenode.path),c.anchor_url(filenode.changeset.raw_id,filenode.path)+"_target")}
102 %else:
102 %else:
103 ${h.link_to(h.safe_unicode(filenode.path),h.url.current(anchor=h.FID('',filenode.path)))}
103 ${h.link_to(h.safe_unicode(filenode.path),h.url.current(anchor=h.FID('',filenode.path)))}
104 %endif
104 %endif
105 </div>
105 </div>
106 <div class="changes">${h.fancy_file_stats(stat)}</div>
106 <div class="changes">${h.fancy_file_stats(stat)}</div>
107 </div>
107 </div>
108 %endfor
108 %endfor
109 % if c.cut_off:
109 % if c.cut_off:
110 ${_('Changeset was too big and was cut off...')}
110 ${_('Changeset was too big and was cut off...')}
111 % endif
111 % endif
112 </div>
112 </div>
113 </div>
113 </div>
114
114
115 </div>
115 </div>
116
116
117 ## diff block
117 ## diff block
118 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
118 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
119 ${diff_block.diff_block(c.changes)}
119 ${diff_block.diff_block(c.changes)}
120
120
121 ## template for inline comment form
121 ## template for inline comment form
122 <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
122 <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
123 ${comment.comment_inline_form(c.changeset)}
123 ${comment.comment_inline_form(c.changeset)}
124
124
125 ${comment.comments(c.changeset)}
125 ${comment.comments(c.changeset)}
126
126
127 <script type="text/javascript">
127 <script type="text/javascript">
128 var deleteComment = function(comment_id){
128 var deleteComment = function(comment_id){
129
129
130 var url = "${url('changeset_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}".replace('__COMMENT_ID__',comment_id);
130 var url = "${url('changeset_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}".replace('__COMMENT_ID__',comment_id);
131 var postData = '_method=delete';
131 var postData = '_method=delete';
132 var success = function(o){
132 var success = function(o){
133 var n = YUD.get('comment-'+comment_id);
133 var n = YUD.get('comment-'+comment_id);
134 n.parentNode.removeChild(n);
134 n.parentNode.removeChild(n);
135 }
135 }
136 ajaxPOST(url,postData,success);
136 ajaxPOST(url,postData,success);
137 }
137 }
138
138
139 YUE.onDOMReady(function(){
139 YUE.onDOMReady(function(){
140
140
141 YUE.on(YUQ('.show-inline-comments'),'change',function(e){
141 YUE.on(YUQ('.show-inline-comments'),'change',function(e){
142 var show = 'none';
142 var show = 'none';
143 var target = e.currentTarget;
143 var target = e.currentTarget;
144 if(target.checked){
144 if(target.checked){
145 var show = ''
145 var show = ''
146 }
146 }
147 var boxid = YUD.getAttribute(target,'id_for');
147 var boxid = YUD.getAttribute(target,'id_for');
148 var comments = YUQ('#{0} .inline-comments'.format(boxid));
148 var comments = YUQ('#{0} .inline-comments'.format(boxid));
149 for(c in comments){
149 for(c in comments){
150 YUD.setStyle(comments[c],'display',show);
150 YUD.setStyle(comments[c],'display',show);
151 }
151 }
152 var btns = YUQ('#{0} .inline-comments-button'.format(boxid));
152 var btns = YUQ('#{0} .inline-comments-button'.format(boxid));
153 for(c in btns){
153 for(c in btns){
154 YUD.setStyle(btns[c],'display',show);
154 YUD.setStyle(btns[c],'display',show);
155 }
155 }
156 })
156 })
157
157
158 YUE.on(YUQ('.line'),'click',function(e){
158 YUE.on(YUQ('.line'),'click',function(e){
159 var tr = e.currentTarget;
159 var tr = e.currentTarget;
160 injectInlineForm(tr);
160 injectInlineForm(tr);
161 });
161 });
162
162
163 // inject comments into they proper positions
163 // inject comments into they proper positions
164 var file_comments = YUQ('.inline-comment-placeholder');
164 var file_comments = YUQ('.inline-comment-placeholder');
165
165
166 for (f in file_comments){
166 for (f in file_comments){
167 var box = file_comments[f];
167 var box = file_comments[f];
168 var inlines = box.children;
168 var inlines = box.children;
169 for(var i=0; i<inlines.length; i++){
169 for(var i=0; i<inlines.length; i++){
170 try{
170 try{
171
171
172 var inline = inlines[i];
172 var inline = inlines[i];
173 var lineno = YUD.getAttribute(inlines[i],'line');
173 var lineno = YUD.getAttribute(inlines[i],'line');
174 var lineid = "{0}_{1}".format(YUD.getAttribute(inline,'target_id'),lineno);
174 var lineid = "{0}_{1}".format(YUD.getAttribute(inline,'target_id'),lineno);
175 var target_line = YUD.get(lineid);
175 var target_line = YUD.get(lineid);
176
176
177 var add = createInlineAddButton(target_line.parentNode,'${_("add another comment")}');
177 var add = createInlineAddButton(target_line.parentNode,'${_("add another comment")}');
178 YUD.insertAfter(add,target_line.parentNode);
178 YUD.insertAfter(add,target_line.parentNode);
179
179
180 var comment = new YAHOO.util.Element(tableTr('inline-comments',inline.innerHTML))
180 var comment = new YAHOO.util.Element(tableTr('inline-comments',inline.innerHTML))
181 YUD.insertAfter(comment,target_line.parentNode);
181 YUD.insertAfter(comment,target_line.parentNode);
182 }catch(e){
182 }catch(e){
183 console.log(e);
183 console.log(e);
184 }
184 }
185 }
185 }
186 }
186 }
187 })
187 })
188
188
189 </script>
189 </script>
190
190
191 </div>
191 </div>
192 </%def>
192 </%def>
General Comments 0
You need to be logged in to leave comments. Login now