##// END OF EJS Templates
Merged in domruf/rhodecode (pull request #64)
marcink -
r2702:48fad3a6 merge beta
parent child Browse files
Show More
@@ -1,527 +1,530 b''
1 1 import os
2 2 import time
3 3 import datetime
4 4 import urllib
5 5 import urllib2
6 6
7 7 from rhodecode.lib.vcs.backends.base import BaseRepository
8 8 from .workdir import MercurialWorkdir
9 9 from .changeset import MercurialChangeset
10 10 from .inmemory import MercurialInMemoryChangeset
11 11
12 12 from rhodecode.lib.vcs.exceptions import BranchDoesNotExistError, \
13 13 ChangesetDoesNotExistError, EmptyRepositoryError, RepositoryError, \
14 14 VCSError, TagAlreadyExistError, TagDoesNotExistError
15 15 from rhodecode.lib.vcs.utils import author_email, author_name, date_fromtimestamp, \
16 16 makedate, safe_unicode
17 17 from rhodecode.lib.vcs.utils.lazy import LazyProperty
18 18 from rhodecode.lib.vcs.utils.ordered_dict import OrderedDict
19 19 from rhodecode.lib.vcs.utils.paths import abspath
20 20
21 21 from rhodecode.lib.vcs.utils.hgcompat import ui, nullid, match, patch, diffopts, clone, \
22 22 get_contact, pull, localrepository, RepoLookupError, Abort, RepoError, hex
23 23
24 24
25 25 class MercurialRepository(BaseRepository):
26 26 """
27 27 Mercurial repository backend
28 28 """
29 29 DEFAULT_BRANCH_NAME = 'default'
30 30 scm = 'hg'
31 31
32 32 def __init__(self, repo_path, create=False, baseui=None, src_url=None,
33 33 update_after_clone=False):
34 34 """
35 35 Raises RepositoryError if repository could not be find at the given
36 36 ``repo_path``.
37 37
38 38 :param repo_path: local path of the repository
39 39 :param create=False: if set to True, would try to create repository if
40 40 it does not exist rather than raising exception
41 41 :param baseui=None: user data
42 42 :param src_url=None: would try to clone repository from given location
43 43 :param update_after_clone=False: sets update of working copy after
44 44 making a clone
45 45 """
46 46
47 47 if not isinstance(repo_path, str):
48 48 raise VCSError('Mercurial backend requires repository path to '
49 49 'be instance of <str> got %s instead' %
50 50 type(repo_path))
51 51
52 52 self.path = abspath(repo_path)
53 53 self.baseui = baseui or ui.ui()
54 54 # We've set path and ui, now we can set _repo itself
55 55 self._repo = self._get_repo(create, src_url, update_after_clone)
56 56
57 57 @property
58 58 def _empty(self):
59 59 """
60 60 Checks if repository is empty without any changesets
61 61 """
62 62 # TODO: Following raises errors when using InMemoryChangeset...
63 63 # return len(self._repo.changelog) == 0
64 64 return len(self.revisions) == 0
65 65
66 66 @LazyProperty
67 67 def revisions(self):
68 68 """
69 69 Returns list of revisions' ids, in ascending order. Being lazy
70 70 attribute allows external tools to inject shas from cache.
71 71 """
72 72 return self._get_all_revisions()
73 73
74 74 @LazyProperty
75 75 def name(self):
76 76 return os.path.basename(self.path)
77 77
78 78 @LazyProperty
79 79 def branches(self):
80 80 return self._get_branches()
81 81
82 82 def _get_branches(self, closed=False):
83 83 """
84 84 Get's branches for this repository
85 85 Returns only not closed branches by default
86 86
87 87 :param closed: return also closed branches for mercurial
88 88 """
89 89
90 90 if self._empty:
91 91 return {}
92 92
93 93 def _branchtags(localrepo):
94 94 """
95 95 Patched version of mercurial branchtags to not return the closed
96 96 branches
97 97
98 98 :param localrepo: locarepository instance
99 99 """
100 100
101 101 bt = {}
102 102 bt_closed = {}
103 103 for bn, heads in localrepo.branchmap().iteritems():
104 104 tip = heads[-1]
105 105 if 'close' in localrepo.changelog.read(tip)[5]:
106 106 bt_closed[bn] = tip
107 107 else:
108 108 bt[bn] = tip
109 109
110 110 if closed:
111 111 bt.update(bt_closed)
112 112 return bt
113 113
114 114 sortkey = lambda ctx: ctx[0] # sort by name
115 115 _branches = [(safe_unicode(n), hex(h),) for n, h in
116 116 _branchtags(self._repo).items()]
117 117
118 118 return OrderedDict(sorted(_branches, key=sortkey, reverse=False))
119 119
120 120 @LazyProperty
121 121 def tags(self):
122 122 """
123 123 Get's tags for this repository
124 124 """
125 125 return self._get_tags()
126 126
127 127 def _get_tags(self):
128 128 if self._empty:
129 129 return {}
130 130
131 131 sortkey = lambda ctx: ctx[0] # sort by name
132 132 _tags = [(safe_unicode(n), hex(h),) for n, h in
133 133 self._repo.tags().items()]
134 134
135 135 return OrderedDict(sorted(_tags, key=sortkey, reverse=True))
136 136
137 137 def tag(self, name, user, revision=None, message=None, date=None,
138 138 **kwargs):
139 139 """
140 140 Creates and returns a tag for the given ``revision``.
141 141
142 142 :param name: name for new tag
143 143 :param user: full username, i.e.: "Joe Doe <joe.doe@example.com>"
144 144 :param revision: changeset id for which new tag would be created
145 145 :param message: message of the tag's commit
146 146 :param date: date of tag's commit
147 147
148 148 :raises TagAlreadyExistError: if tag with same name already exists
149 149 """
150 150 if name in self.tags:
151 151 raise TagAlreadyExistError("Tag %s already exists" % name)
152 152 changeset = self.get_changeset(revision)
153 153 local = kwargs.setdefault('local', False)
154 154
155 155 if message is None:
156 156 message = "Added tag %s for changeset %s" % (name,
157 157 changeset.short_id)
158 158
159 159 if date is None:
160 160 date = datetime.datetime.now().ctime()
161 161
162 162 try:
163 163 self._repo.tag(name, changeset._ctx.node(), message, local, user,
164 164 date)
165 165 except Abort, e:
166 166 raise RepositoryError(e.message)
167 167
168 168 # Reinitialize tags
169 169 self.tags = self._get_tags()
170 170 tag_id = self.tags[name]
171 171
172 172 return self.get_changeset(revision=tag_id)
173 173
174 174 def remove_tag(self, name, user, message=None, date=None):
175 175 """
176 176 Removes tag with the given ``name``.
177 177
178 178 :param name: name of the tag to be removed
179 179 :param user: full username, i.e.: "Joe Doe <joe.doe@example.com>"
180 180 :param message: message of the tag's removal commit
181 181 :param date: date of tag's removal commit
182 182
183 183 :raises TagDoesNotExistError: if tag with given name does not exists
184 184 """
185 185 if name not in self.tags:
186 186 raise TagDoesNotExistError("Tag %s does not exist" % name)
187 187 if message is None:
188 188 message = "Removed tag %s" % name
189 189 if date is None:
190 190 date = datetime.datetime.now().ctime()
191 191 local = False
192 192
193 193 try:
194 194 self._repo.tag(name, nullid, message, local, user, date)
195 195 self.tags = self._get_tags()
196 196 except Abort, e:
197 197 raise RepositoryError(e.message)
198 198
199 199 @LazyProperty
200 200 def bookmarks(self):
201 201 """
202 202 Get's bookmarks for this repository
203 203 """
204 204 return self._get_bookmarks()
205 205
206 206 def _get_bookmarks(self):
207 207 if self._empty:
208 208 return {}
209 209
210 210 sortkey = lambda ctx: ctx[0] # sort by name
211 211 _bookmarks = [(safe_unicode(n), hex(h),) for n, h in
212 212 self._repo._bookmarks.items()]
213 213 return OrderedDict(sorted(_bookmarks, key=sortkey, reverse=True))
214 214
215 215 def _get_all_revisions(self):
216 216
217 217 return map(lambda x: hex(x[7]), self._repo.changelog.index)[:-1]
218 218
219 219 def get_diff(self, rev1, rev2, path='', ignore_whitespace=False,
220 220 context=3):
221 221 """
222 222 Returns (git like) *diff*, as plain text. Shows changes introduced by
223 223 ``rev2`` since ``rev1``.
224 224
225 225 :param rev1: Entry point from which diff is shown. Can be
226 226 ``self.EMPTY_CHANGESET`` - in this case, patch showing all
227 227 the changes since empty state of the repository until ``rev2``
228 228 :param rev2: Until which revision changes should be shown.
229 229 :param ignore_whitespace: If set to ``True``, would not show whitespace
230 230 changes. Defaults to ``False``.
231 231 :param context: How many lines before/after changed lines should be
232 232 shown. Defaults to ``3``.
233 233 """
234 234 if hasattr(rev1, 'raw_id'):
235 235 rev1 = getattr(rev1, 'raw_id')
236 236
237 237 if hasattr(rev2, 'raw_id'):
238 238 rev2 = getattr(rev2, 'raw_id')
239 239
240 240 # Check if given revisions are present at repository (may raise
241 241 # ChangesetDoesNotExistError)
242 242 if rev1 != self.EMPTY_CHANGESET:
243 243 self.get_changeset(rev1)
244 244 self.get_changeset(rev2)
245 245
246 246 file_filter = match(self.path, '', [path])
247 247 return ''.join(patch.diff(self._repo, rev1, rev2, match=file_filter,
248 248 opts=diffopts(git=True,
249 249 ignorews=ignore_whitespace,
250 250 context=context)))
251 251
252 252 def _check_url(self, url):
253 253 """
254 254 Function will check given url and try to verify if it's a valid
255 255 link. Sometimes it may happened that mercurial will issue basic
256 256 auth request that can cause whole API to hang when used from python
257 257 or other external calls.
258 258
259 259 On failures it'll raise urllib2.HTTPError, return code 200 if url
260 260 is valid or True if it's a local path
261 261 """
262 262
263 263 from mercurial.util import url as Url
264 264
265 265 # those authnadlers are patched for python 2.6.5 bug an
266 266 # infinit looping when given invalid resources
267 267 from mercurial.url import httpbasicauthhandler, httpdigestauthhandler
268 268
269 269 # check first if it's not an local url
270 270 if os.path.isdir(url) or url.startswith('file:'):
271 271 return True
272 272
273 if('+' in url[:url.find('://')]):
274 url = url[url.find('+')+1:]
275
273 276 handlers = []
274 277 test_uri, authinfo = Url(url).authinfo()
275 278
276 279 if authinfo:
277 280 #create a password manager
278 281 passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
279 282 passmgr.add_password(*authinfo)
280 283
281 284 handlers.extend((httpbasicauthhandler(passmgr),
282 285 httpdigestauthhandler(passmgr)))
283 286
284 287 o = urllib2.build_opener(*handlers)
285 288 o.addheaders = [('Content-Type', 'application/mercurial-0.1'),
286 289 ('Accept', 'application/mercurial-0.1')]
287 290
288 291 q = {"cmd": 'between'}
289 292 q.update({'pairs': "%s-%s" % ('0' * 40, '0' * 40)})
290 293 qs = '?%s' % urllib.urlencode(q)
291 294 cu = "%s%s" % (test_uri, qs)
292 295 req = urllib2.Request(cu, None, {})
293 296
294 297 try:
295 298 resp = o.open(req)
296 299 return resp.code == 200
297 300 except Exception, e:
298 301 # means it cannot be cloned
299 302 raise urllib2.URLError(e)
300 303
301 304 def _get_repo(self, create, src_url=None, update_after_clone=False):
302 305 """
303 306 Function will check for mercurial repository in given path and return
304 307 a localrepo object. If there is no repository in that path it will
305 308 raise an exception unless ``create`` parameter is set to True - in
306 309 that case repository would be created and returned.
307 310 If ``src_url`` is given, would try to clone repository from the
308 311 location at given clone_point. Additionally it'll make update to
309 312 working copy accordingly to ``update_after_clone`` flag
310 313 """
311 314 try:
312 315 if src_url:
313 316 url = str(self._get_url(src_url))
314 317 opts = {}
315 318 if not update_after_clone:
316 319 opts.update({'noupdate': True})
317 320 try:
318 321 self._check_url(url)
319 322 clone(self.baseui, url, self.path, **opts)
320 323 # except urllib2.URLError:
321 324 # raise Abort("Got HTTP 404 error")
322 325 except Exception:
323 326 raise
324 327 # Don't try to create if we've already cloned repo
325 328 create = False
326 329 return localrepository(self.baseui, self.path, create=create)
327 330 except (Abort, RepoError), err:
328 331 if create:
329 332 msg = "Cannot create repository at %s. Original error was %s"\
330 333 % (self.path, err)
331 334 else:
332 335 msg = "Not valid repository at %s. Original error was %s"\
333 336 % (self.path, err)
334 337 raise RepositoryError(msg)
335 338
336 339 @LazyProperty
337 340 def in_memory_changeset(self):
338 341 return MercurialInMemoryChangeset(self)
339 342
340 343 @LazyProperty
341 344 def description(self):
342 345 undefined_description = u'unknown'
343 346 return safe_unicode(self._repo.ui.config('web', 'description',
344 347 undefined_description, untrusted=True))
345 348
346 349 @LazyProperty
347 350 def contact(self):
348 351 undefined_contact = u'Unknown'
349 352 return safe_unicode(get_contact(self._repo.ui.config)
350 353 or undefined_contact)
351 354
352 355 @LazyProperty
353 356 def last_change(self):
354 357 """
355 358 Returns last change made on this repository as datetime object
356 359 """
357 360 return date_fromtimestamp(self._get_mtime(), makedate()[1])
358 361
359 362 def _get_mtime(self):
360 363 try:
361 364 return time.mktime(self.get_changeset().date.timetuple())
362 365 except RepositoryError:
363 366 #fallback to filesystem
364 367 cl_path = os.path.join(self.path, '.hg', "00changelog.i")
365 368 st_path = os.path.join(self.path, '.hg', "store")
366 369 if os.path.exists(cl_path):
367 370 return os.stat(cl_path).st_mtime
368 371 else:
369 372 return os.stat(st_path).st_mtime
370 373
371 374 def _get_hidden(self):
372 375 return self._repo.ui.configbool("web", "hidden", untrusted=True)
373 376
374 377 def _get_revision(self, revision):
375 378 """
376 379 Get's an ID revision given as str. This will always return a fill
377 380 40 char revision number
378 381
379 382 :param revision: str or int or None
380 383 """
381 384
382 385 if self._empty:
383 386 raise EmptyRepositoryError("There are no changesets yet")
384 387
385 388 if revision in [-1, 'tip', None]:
386 389 revision = 'tip'
387 390
388 391 try:
389 392 revision = hex(self._repo.lookup(revision))
390 393 except (IndexError, ValueError, RepoLookupError, TypeError):
391 394 raise ChangesetDoesNotExistError("Revision %r does not "
392 395 "exist for this repository %s" \
393 396 % (revision, self))
394 397 return revision
395 398
396 399 def _get_archives(self, archive_name='tip'):
397 400 allowed = self.baseui.configlist("web", "allow_archive",
398 401 untrusted=True)
399 402 for i in [('zip', '.zip'), ('gz', '.tar.gz'), ('bz2', '.tar.bz2')]:
400 403 if i[0] in allowed or self._repo.ui.configbool("web",
401 404 "allow" + i[0],
402 405 untrusted=True):
403 406 yield {"type": i[0], "extension": i[1], "node": archive_name}
404 407
405 408 def _get_url(self, url):
406 409 """
407 410 Returns normalized url. If schema is not given, would fall
408 411 to filesystem
409 412 (``file:///``) schema.
410 413 """
411 414 url = str(url)
412 415 if url != 'default' and not '://' in url:
413 416 url = "file:" + urllib.pathname2url(url)
414 417 return url
415 418
416 419 def get_changeset(self, revision=None):
417 420 """
418 421 Returns ``MercurialChangeset`` object representing repository's
419 422 changeset at the given ``revision``.
420 423 """
421 424 revision = self._get_revision(revision)
422 425 changeset = MercurialChangeset(repository=self, revision=revision)
423 426 return changeset
424 427
425 428 def get_changesets(self, start=None, end=None, start_date=None,
426 429 end_date=None, branch_name=None, reverse=False):
427 430 """
428 431 Returns iterator of ``MercurialChangeset`` objects from start to end
429 432 (both are inclusive)
430 433
431 434 :param start: None, str, int or mercurial lookup format
432 435 :param end: None, str, int or mercurial lookup format
433 436 :param start_date:
434 437 :param end_date:
435 438 :param branch_name:
436 439 :param reversed: return changesets in reversed order
437 440 """
438 441
439 442 start_raw_id = self._get_revision(start)
440 443 start_pos = self.revisions.index(start_raw_id) if start else None
441 444 end_raw_id = self._get_revision(end)
442 445 end_pos = self.revisions.index(end_raw_id) if end else None
443 446
444 447 if None not in [start, end] and start_pos > end_pos:
445 448 raise RepositoryError("start revision '%s' cannot be "
446 449 "after end revision '%s'" % (start, end))
447 450
448 451 if branch_name and branch_name not in self.branches.keys():
449 452 raise BranchDoesNotExistError('Such branch %s does not exists for'
450 453 ' this repository' % branch_name)
451 454 if end_pos is not None:
452 455 end_pos += 1
453 456
454 457 slice_ = reversed(self.revisions[start_pos:end_pos]) if reverse else \
455 458 self.revisions[start_pos:end_pos]
456 459
457 460 for id_ in slice_:
458 461 cs = self.get_changeset(id_)
459 462 if branch_name and cs.branch != branch_name:
460 463 continue
461 464 if start_date and cs.date < start_date:
462 465 continue
463 466 if end_date and cs.date > end_date:
464 467 continue
465 468
466 469 yield cs
467 470
468 471 def pull(self, url):
469 472 """
470 473 Tries to pull changes from external location.
471 474 """
472 475 url = self._get_url(url)
473 476 try:
474 477 pull(self.baseui, self._repo, url)
475 478 except Abort, err:
476 479 # Propagate error but with vcs's type
477 480 raise RepositoryError(str(err))
478 481
479 482 @LazyProperty
480 483 def workdir(self):
481 484 """
482 485 Returns ``Workdir`` instance for this repository.
483 486 """
484 487 return MercurialWorkdir(self)
485 488
486 489 def get_config_value(self, section, name, config_file=None):
487 490 """
488 491 Returns configuration value for a given [``section``] and ``name``.
489 492
490 493 :param section: Section we want to retrieve value from
491 494 :param name: Name of configuration we want to retrieve
492 495 :param config_file: A path to file which should be used to retrieve
493 496 configuration from (might also be a list of file paths)
494 497 """
495 498 if config_file is None:
496 499 config_file = []
497 500 elif isinstance(config_file, basestring):
498 501 config_file = [config_file]
499 502
500 503 config = self._repo.ui
501 504 for path in config_file:
502 505 config.readconfig(path)
503 506 return config.config(section, name)
504 507
505 508 def get_user_name(self, config_file=None):
506 509 """
507 510 Returns user's name from global configuration file.
508 511
509 512 :param config_file: A path to file which should be used to retrieve
510 513 configuration from (might also be a list of file paths)
511 514 """
512 515 username = self.get_config_value('ui', 'username')
513 516 if username:
514 517 return author_name(username)
515 518 return None
516 519
517 520 def get_user_email(self, config_file=None):
518 521 """
519 522 Returns user's email from global configuration file.
520 523
521 524 :param config_file: A path to file which should be used to retrieve
522 525 configuration from (might also be a list of file paths)
523 526 """
524 527 username = self.get_config_value('ui', 'username')
525 528 if username:
526 529 return author_email(username)
527 530 return None
@@ -1,593 +1,590 b''
1 1 """
2 2 Set of generic validators
3 3 """
4 4 import os
5 5 import re
6 6 import formencode
7 7 import logging
8 8 from pylons.i18n.translation import _
9 9 from webhelpers.pylonslib.secure_form import authentication_token
10 10
11 11 from formencode.validators import (
12 12 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set
13 13 )
14 14
15 15 from rhodecode.lib.utils import repo_name_slug
16 16 from rhodecode.model.db import RepoGroup, Repository, UsersGroup, User
17 17 from rhodecode.lib.exceptions import LdapImportError
18 18 from rhodecode.config.routing import ADMIN_PREFIX
19 19 # silence warnings and pylint
20 20 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set
21 21
22 22 log = logging.getLogger(__name__)
23 23
24 24
25 25 class StateObj(object):
26 26 """
27 27 this is needed to translate the messages using _() in validators
28 28 """
29 29 _ = staticmethod(_)
30 30
31 31
32 32 def M(self, key, state=None, **kwargs):
33 33 """
34 34 returns string from self.message based on given key,
35 35 passed kw params are used to substitute %(named)s params inside
36 36 translated strings
37 37
38 38 :param msg:
39 39 :param state:
40 40 """
41 41 if state is None:
42 42 state = StateObj()
43 43 else:
44 44 state._ = staticmethod(_)
45 45 #inject validator into state object
46 46 return self.message(key, state, **kwargs)
47 47
48 48
49 49 def ValidUsername(edit=False, old_data={}):
50 50 class _validator(formencode.validators.FancyValidator):
51 51 messages = {
52 52 'username_exists': _(u'Username "%(username)s" already exists'),
53 53 'system_invalid_username':
54 54 _(u'Username "%(username)s" is forbidden'),
55 55 'invalid_username':
56 56 _(u'Username may only contain alphanumeric characters '
57 57 'underscores, periods or dashes and must begin with '
58 58 'alphanumeric character')
59 59 }
60 60
61 61 def validate_python(self, value, state):
62 62 if value in ['default', 'new_user']:
63 63 msg = M(self, 'system_invalid_username', state, username=value)
64 64 raise formencode.Invalid(msg, value, state)
65 65 #check if user is unique
66 66 old_un = None
67 67 if edit:
68 68 old_un = User.get(old_data.get('user_id')).username
69 69
70 70 if old_un != value or not edit:
71 71 if User.get_by_username(value, case_insensitive=True):
72 72 msg = M(self, 'username_exists', state, username=value)
73 73 raise formencode.Invalid(msg, value, state)
74 74
75 75 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
76 76 msg = M(self, 'invalid_username', state)
77 77 raise formencode.Invalid(msg, value, state)
78 78 return _validator
79 79
80 80
81 81 def ValidRepoUser():
82 82 class _validator(formencode.validators.FancyValidator):
83 83 messages = {
84 84 'invalid_username': _(u'Username %(username)s is not valid')
85 85 }
86 86
87 87 def validate_python(self, value, state):
88 88 try:
89 89 User.query().filter(User.active == True)\
90 90 .filter(User.username == value).one()
91 91 except Exception:
92 92 msg = M(self, 'invalid_username', state, username=value)
93 93 raise formencode.Invalid(msg, value, state,
94 94 error_dict=dict(username=msg)
95 95 )
96 96
97 97 return _validator
98 98
99 99
100 100 def ValidUsersGroup(edit=False, old_data={}):
101 101 class _validator(formencode.validators.FancyValidator):
102 102 messages = {
103 103 'invalid_group': _(u'Invalid users group name'),
104 104 'group_exist': _(u'Users group "%(usersgroup)s" already exists'),
105 105 'invalid_usersgroup_name':
106 106 _(u'users group name may only contain alphanumeric '
107 107 'characters underscores, periods or dashes and must begin '
108 108 'with alphanumeric character')
109 109 }
110 110
111 111 def validate_python(self, value, state):
112 112 if value in ['default']:
113 113 msg = M(self, 'invalid_group', state)
114 114 raise formencode.Invalid(msg, value, state,
115 115 error_dict=dict(users_group_name=msg)
116 116 )
117 117 #check if group is unique
118 118 old_ugname = None
119 119 if edit:
120 120 old_id = old_data.get('users_group_id')
121 121 old_ugname = UsersGroup.get(old_id).users_group_name
122 122
123 123 if old_ugname != value or not edit:
124 124 is_existing_group = UsersGroup.get_by_group_name(value,
125 125 case_insensitive=True)
126 126 if is_existing_group:
127 127 msg = M(self, 'group_exist', state, usersgroup=value)
128 128 raise formencode.Invalid(msg, value, state,
129 129 error_dict=dict(users_group_name=msg)
130 130 )
131 131
132 132 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
133 133 msg = M(self, 'invalid_usersgroup_name', state)
134 134 raise formencode.Invalid(msg, value, state,
135 135 error_dict=dict(users_group_name=msg)
136 136 )
137 137
138 138 return _validator
139 139
140 140
141 141 def ValidReposGroup(edit=False, old_data={}):
142 142 class _validator(formencode.validators.FancyValidator):
143 143 messages = {
144 144 'group_parent_id': _(u'Cannot assign this group as parent'),
145 145 'group_exists': _(u'Group "%(group_name)s" already exists'),
146 146 'repo_exists':
147 147 _(u'Repository with name "%(group_name)s" already exists')
148 148 }
149 149
150 150 def validate_python(self, value, state):
151 151 # TODO WRITE VALIDATIONS
152 152 group_name = value.get('group_name')
153 153 group_parent_id = value.get('group_parent_id')
154 154
155 155 # slugify repo group just in case :)
156 156 slug = repo_name_slug(group_name)
157 157
158 158 # check for parent of self
159 159 parent_of_self = lambda: (
160 160 old_data['group_id'] == int(group_parent_id)
161 161 if group_parent_id else False
162 162 )
163 163 if edit and parent_of_self():
164 164 msg = M(self, 'group_parent_id', state)
165 165 raise formencode.Invalid(msg, value, state,
166 166 error_dict=dict(group_parent_id=msg)
167 167 )
168 168
169 169 old_gname = None
170 170 if edit:
171 171 old_gname = RepoGroup.get(old_data.get('group_id')).group_name
172 172
173 173 if old_gname != group_name or not edit:
174 174
175 175 # check group
176 176 gr = RepoGroup.query()\
177 177 .filter(RepoGroup.group_name == slug)\
178 178 .filter(RepoGroup.group_parent_id == group_parent_id)\
179 179 .scalar()
180 180
181 181 if gr:
182 182 msg = M(self, 'group_exists', state, group_name=slug)
183 183 raise formencode.Invalid(msg, value, state,
184 184 error_dict=dict(group_name=msg)
185 185 )
186 186
187 187 # check for same repo
188 188 repo = Repository.query()\
189 189 .filter(Repository.repo_name == slug)\
190 190 .scalar()
191 191
192 192 if repo:
193 193 msg = M(self, 'repo_exists', state, group_name=slug)
194 194 raise formencode.Invalid(msg, value, state,
195 195 error_dict=dict(group_name=msg)
196 196 )
197 197
198 198 return _validator
199 199
200 200
201 201 def ValidPassword():
202 202 class _validator(formencode.validators.FancyValidator):
203 203 messages = {
204 204 'invalid_password':
205 205 _(u'Invalid characters (non-ascii) in password')
206 206 }
207 207
208 208 def validate_python(self, value, state):
209 209 try:
210 210 (value or '').decode('ascii')
211 211 except UnicodeError:
212 212 msg = M(self, 'invalid_password', state)
213 213 raise formencode.Invalid(msg, value, state,)
214 214 return _validator
215 215
216 216
217 217 def ValidPasswordsMatch():
218 218 class _validator(formencode.validators.FancyValidator):
219 219 messages = {
220 220 'password_mismatch': _(u'Passwords do not match'),
221 221 }
222 222
223 223 def validate_python(self, value, state):
224 224
225 225 pass_val = value.get('password') or value.get('new_password')
226 226 if pass_val != value['password_confirmation']:
227 227 msg = M(self, 'password_mismatch', state)
228 228 raise formencode.Invalid(msg, value, state,
229 229 error_dict=dict(password_confirmation=msg)
230 230 )
231 231 return _validator
232 232
233 233
234 234 def ValidAuth():
235 235 class _validator(formencode.validators.FancyValidator):
236 236 messages = {
237 237 'invalid_password': _(u'invalid password'),
238 238 'invalid_username': _(u'invalid user name'),
239 239 'disabled_account': _(u'Your account is disabled')
240 240 }
241 241
242 242 def validate_python(self, value, state):
243 243 from rhodecode.lib.auth import authenticate
244 244
245 245 password = value['password']
246 246 username = value['username']
247 247
248 248 if not authenticate(username, password):
249 249 user = User.get_by_username(username)
250 250 if user and user.active is False:
251 251 log.warning('user %s is disabled' % username)
252 252 msg = M(self, 'disabled_account', state)
253 253 raise formencode.Invalid(msg, value, state,
254 254 error_dict=dict(username=msg)
255 255 )
256 256 else:
257 257 log.warning('user %s failed to authenticate' % username)
258 258 msg = M(self, 'invalid_username', state)
259 259 msg2 = M(self, 'invalid_password', state)
260 260 raise formencode.Invalid(msg, value, state,
261 261 error_dict=dict(username=msg, password=msg2)
262 262 )
263 263 return _validator
264 264
265 265
266 266 def ValidAuthToken():
267 267 class _validator(formencode.validators.FancyValidator):
268 268 messages = {
269 269 'invalid_token': _(u'Token mismatch')
270 270 }
271 271
272 272 def validate_python(self, value, state):
273 273 if value != authentication_token():
274 274 msg = M(self, 'invalid_token', state)
275 275 raise formencode.Invalid(msg, value, state)
276 276 return _validator
277 277
278 278
279 279 def ValidRepoName(edit=False, old_data={}):
280 280 class _validator(formencode.validators.FancyValidator):
281 281 messages = {
282 282 'invalid_repo_name':
283 283 _(u'Repository name %(repo)s is disallowed'),
284 284 'repository_exists':
285 285 _(u'Repository named %(repo)s already exists'),
286 286 'repository_in_group_exists': _(u'Repository "%(repo)s" already '
287 287 'exists in group "%(group)s"'),
288 288 'same_group_exists': _(u'Repositories group with name "%(repo)s" '
289 289 'already exists')
290 290 }
291 291
292 292 def _to_python(self, value, state):
293 293 repo_name = repo_name_slug(value.get('repo_name', ''))
294 294 repo_group = value.get('repo_group')
295 295 if repo_group:
296 296 gr = RepoGroup.get(repo_group)
297 297 group_path = gr.full_path
298 298 group_name = gr.group_name
299 299 # value needs to be aware of group name in order to check
300 300 # db key This is an actual just the name to store in the
301 301 # database
302 302 repo_name_full = group_path + RepoGroup.url_sep() + repo_name
303 303 else:
304 304 group_name = group_path = ''
305 305 repo_name_full = repo_name
306 306
307 307 value['repo_name'] = repo_name
308 308 value['repo_name_full'] = repo_name_full
309 309 value['group_path'] = group_path
310 310 value['group_name'] = group_name
311 311 return value
312 312
313 313 def validate_python(self, value, state):
314 314
315 315 repo_name = value.get('repo_name')
316 316 repo_name_full = value.get('repo_name_full')
317 317 group_path = value.get('group_path')
318 318 group_name = value.get('group_name')
319 319
320 320 if repo_name in [ADMIN_PREFIX, '']:
321 321 msg = M(self, 'invalid_repo_name', state, repo=repo_name)
322 322 raise formencode.Invalid(msg, value, state,
323 323 error_dict=dict(repo_name=msg)
324 324 )
325 325
326 326 rename = old_data.get('repo_name') != repo_name_full
327 327 create = not edit
328 328 if rename or create:
329 329
330 330 if group_path != '':
331 331 if Repository.get_by_repo_name(repo_name_full):
332 332 msg = M(self, 'repository_in_group_exists', state,
333 333 repo=repo_name, group=group_name)
334 334 raise formencode.Invalid(msg, value, state,
335 335 error_dict=dict(repo_name=msg)
336 336 )
337 337 elif RepoGroup.get_by_group_name(repo_name_full):
338 338 msg = M(self, 'same_group_exists', state,
339 339 repo=repo_name)
340 340 raise formencode.Invalid(msg, value, state,
341 341 error_dict=dict(repo_name=msg)
342 342 )
343 343
344 344 elif Repository.get_by_repo_name(repo_name_full):
345 345 msg = M(self, 'repository_exists', state,
346 346 repo=repo_name)
347 347 raise formencode.Invalid(msg, value, state,
348 348 error_dict=dict(repo_name=msg)
349 349 )
350 350 return value
351 351 return _validator
352 352
353 353
354 354 def ValidForkName(*args, **kwargs):
355 355 return ValidRepoName(*args, **kwargs)
356 356
357 357
358 358 def SlugifyName():
359 359 class _validator(formencode.validators.FancyValidator):
360 360
361 361 def _to_python(self, value, state):
362 362 return repo_name_slug(value)
363 363
364 364 def validate_python(self, value, state):
365 365 pass
366 366
367 367 return _validator
368 368
369 369
370 370 def ValidCloneUri():
371 371 from rhodecode.lib.utils import make_ui
372 372
373 def url_handler(repo_type, url, proto, ui=None):
373 def url_handler(repo_type, url, ui=None):
374 374 if repo_type == 'hg':
375 375 from mercurial.httprepo import httprepository, httpsrepository
376 if proto == 'https':
376 if url.startswith('https'):
377 377 httpsrepository(make_ui('db'), url).capabilities
378 elif proto == 'http':
378 elif url.startswith('http'):
379 379 httprepository(make_ui('db'), url).capabilities
380 elif url.startswith('svn+http'):
381 from hgsubversion.svnrepo import svnremoterepo
382 svnremoterepo(make_ui('db'), url).capabilities
380 383 elif repo_type == 'git':
381 384 #TODO: write a git url validator
382 385 pass
383 386
384 387 class _validator(formencode.validators.FancyValidator):
385 388 messages = {
386 389 'clone_uri': _(u'invalid clone url'),
387 390 'invalid_clone_uri': _(u'Invalid clone url, provide a '
388 'valid clone http\s url')
391 'valid clone http(s)/svn+http(s) url')
389 392 }
390 393
391 394 def validate_python(self, value, state):
392 395 repo_type = value.get('repo_type')
393 396 url = value.get('clone_uri')
394 397
395 398 if not url:
396 399 pass
397 elif url.startswith('https') or url.startswith('http'):
398 _type = 'https' if url.startswith('https') else 'http'
400 else:
399 401 try:
400 url_handler(repo_type, url, _type, make_ui('db'))
402 url_handler(repo_type, url, make_ui('db'))
401 403 except Exception:
402 404 log.exception('Url validation failed')
403 405 msg = M(self, 'clone_uri')
404 406 raise formencode.Invalid(msg, value, state,
405 407 error_dict=dict(clone_uri=msg)
406 408 )
407 else:
408 msg = M(self, 'invalid_clone_uri', state)
409 raise formencode.Invalid(msg, value, state,
410 error_dict=dict(clone_uri=msg)
411 )
412 409 return _validator
413 410
414 411
415 412 def ValidForkType(old_data={}):
416 413 class _validator(formencode.validators.FancyValidator):
417 414 messages = {
418 415 'invalid_fork_type': _(u'Fork have to be the same type as parent')
419 416 }
420 417
421 418 def validate_python(self, value, state):
422 419 if old_data['repo_type'] != value:
423 420 msg = M(self, 'invalid_fork_type', state)
424 421 raise formencode.Invalid(msg, value, state,
425 422 error_dict=dict(repo_type=msg)
426 423 )
427 424 return _validator
428 425
429 426
430 427 def ValidPerms(type_='repo'):
431 428 if type_ == 'group':
432 429 EMPTY_PERM = 'group.none'
433 430 elif type_ == 'repo':
434 431 EMPTY_PERM = 'repository.none'
435 432
436 433 class _validator(formencode.validators.FancyValidator):
437 434 messages = {
438 435 'perm_new_member_name':
439 436 _(u'This username or users group name is not valid')
440 437 }
441 438
442 439 def to_python(self, value, state):
443 440 perms_update = []
444 441 perms_new = []
445 442 # build a list of permission to update and new permission to create
446 443 for k, v in value.items():
447 444 # means new added member to permissions
448 445 if k.startswith('perm_new_member'):
449 446 new_perm = value.get('perm_new_member', False)
450 447 new_member = value.get('perm_new_member_name', False)
451 448 new_type = value.get('perm_new_member_type')
452 449
453 450 if new_member and new_perm:
454 451 if (new_member, new_perm, new_type) not in perms_new:
455 452 perms_new.append((new_member, new_perm, new_type))
456 453 elif k.startswith('u_perm_') or k.startswith('g_perm_'):
457 454 member = k[7:]
458 455 t = {'u': 'user',
459 456 'g': 'users_group'
460 457 }[k[0]]
461 458 if member == 'default':
462 459 if value.get('private'):
463 460 # set none for default when updating to
464 461 # private repo
465 462 v = EMPTY_PERM
466 463 perms_update.append((member, v, t))
467 464
468 465 value['perms_updates'] = perms_update
469 466 value['perms_new'] = perms_new
470 467
471 468 # update permissions
472 469 for k, v, t in perms_new:
473 470 try:
474 471 if t is 'user':
475 472 self.user_db = User.query()\
476 473 .filter(User.active == True)\
477 474 .filter(User.username == k).one()
478 475 if t is 'users_group':
479 476 self.user_db = UsersGroup.query()\
480 477 .filter(UsersGroup.users_group_active == True)\
481 478 .filter(UsersGroup.users_group_name == k).one()
482 479
483 480 except Exception:
484 481 log.exception('Updated permission failed')
485 482 msg = M(self, 'perm_new_member_type', state)
486 483 raise formencode.Invalid(msg, value, state,
487 484 error_dict=dict(perm_new_member_name=msg)
488 485 )
489 486 return value
490 487 return _validator
491 488
492 489
493 490 def ValidSettings():
494 491 class _validator(formencode.validators.FancyValidator):
495 492 def _to_python(self, value, state):
496 493 # settings form can't edit user
497 494 if 'user' in value:
498 495 del value['user']
499 496 return value
500 497
501 498 def validate_python(self, value, state):
502 499 pass
503 500 return _validator
504 501
505 502
506 503 def ValidPath():
507 504 class _validator(formencode.validators.FancyValidator):
508 505 messages = {
509 506 'invalid_path': _(u'This is not a valid path')
510 507 }
511 508
512 509 def validate_python(self, value, state):
513 510 if not os.path.isdir(value):
514 511 msg = M(self, 'invalid_path', state)
515 512 raise formencode.Invalid(msg, value, state,
516 513 error_dict=dict(paths_root_path=msg)
517 514 )
518 515 return _validator
519 516
520 517
521 518 def UniqSystemEmail(old_data={}):
522 519 class _validator(formencode.validators.FancyValidator):
523 520 messages = {
524 521 'email_taken': _(u'This e-mail address is already taken')
525 522 }
526 523
527 524 def _to_python(self, value, state):
528 525 return value.lower()
529 526
530 527 def validate_python(self, value, state):
531 528 if (old_data.get('email') or '').lower() != value:
532 529 user = User.get_by_email(value, case_insensitive=True)
533 530 if user:
534 531 msg = M(self, 'email_taken', state)
535 532 raise formencode.Invalid(msg, value, state,
536 533 error_dict=dict(email=msg)
537 534 )
538 535 return _validator
539 536
540 537
541 538 def ValidSystemEmail():
542 539 class _validator(formencode.validators.FancyValidator):
543 540 messages = {
544 541 'non_existing_email': _(u'e-mail "%(email)s" does not exist.')
545 542 }
546 543
547 544 def _to_python(self, value, state):
548 545 return value.lower()
549 546
550 547 def validate_python(self, value, state):
551 548 user = User.get_by_email(value, case_insensitive=True)
552 549 if user is None:
553 550 msg = M(self, 'non_existing_email', state, email=value)
554 551 raise formencode.Invalid(msg, value, state,
555 552 error_dict=dict(email=msg)
556 553 )
557 554
558 555 return _validator
559 556
560 557
561 558 def LdapLibValidator():
562 559 class _validator(formencode.validators.FancyValidator):
563 560 messages = {
564 561
565 562 }
566 563
567 564 def validate_python(self, value, state):
568 565 try:
569 566 import ldap
570 567 ldap # pyflakes silence !
571 568 except ImportError:
572 569 raise LdapImportError()
573 570
574 571 return _validator
575 572
576 573
577 574 def AttrLoginValidator():
578 575 class _validator(formencode.validators.FancyValidator):
579 576 messages = {
580 577 'invalid_cn':
581 578 _(u'The LDAP Login attribute of the CN must be specified - '
582 579 'this is the name of the attribute that is equivalent '
583 580 'to "username"')
584 581 }
585 582
586 583 def validate_python(self, value, state):
587 584 if not value or not isinstance(value, (str, unicode)):
588 585 msg = M(self, 'invalid_cn', state)
589 586 raise formencode.Invalid(msg, value, state,
590 587 error_dict=dict(ldap_attr_login=msg)
591 588 )
592 589
593 590 return _validator
General Comments 0
You need to be logged in to leave comments. Login now