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