Show More
@@ -235,7 +235,7 b' INPUT::' | |||||
235 | method : "add_user_users_group" |
|
235 | method : "add_user_users_group" | |
236 | args: { |
|
236 | args: { | |
237 | "group_name" : "<groupname>", |
|
237 | "group_name" : "<groupname>", | |
238 |
"user |
|
238 | "username" : "<username>" | |
239 | } |
|
239 | } | |
240 |
|
240 | |||
241 | OUTPUT:: |
|
241 | OUTPUT:: | |
@@ -316,6 +316,38 b' OUTPUT::' | |||||
316 | } |
|
316 | } | |
317 | error: null |
|
317 | error: null | |
318 |
|
318 | |||
|
319 | get_repo_nodes | |||
|
320 | -------------- | |||
|
321 | ||||
|
322 | returns a list of nodes and it's children in a flat list for a given path | |||
|
323 | at given revision. It's possible to specify ret_type to show only files or | |||
|
324 | dirs. This command can be executed only using api_key belonging to user | |||
|
325 | with admin rights | |||
|
326 | ||||
|
327 | INPUT:: | |||
|
328 | ||||
|
329 | api_key : "<api_key>" | |||
|
330 | method : "get_repo_nodes" | |||
|
331 | args: { | |||
|
332 | "repo_name" : "<name>", | |||
|
333 | "revision" : "<revision>", | |||
|
334 | "root_path" : "<root_path>", | |||
|
335 | "ret_type" : "<ret_type>" = 'all' | |||
|
336 | } | |||
|
337 | ||||
|
338 | OUTPUT:: | |||
|
339 | ||||
|
340 | result: [ | |||
|
341 | { | |||
|
342 | "name" : "<name>" | |||
|
343 | "type" : "<type>", | |||
|
344 | }, | |||
|
345 | … | |||
|
346 | ] | |||
|
347 | error: null | |||
|
348 | ||||
|
349 | ||||
|
350 | ||||
319 | create_repo |
|
351 | create_repo | |
320 | ----------- |
|
352 | ----------- | |
321 |
|
353 | |||
@@ -355,7 +387,7 b' INPUT::' | |||||
355 | method : "add_user_to_repo" |
|
387 | method : "add_user_to_repo" | |
356 | args: { |
|
388 | args: { | |
357 | "repo_name" : "<reponame>", |
|
389 | "repo_name" : "<reponame>", | |
358 |
"user |
|
390 | "username" : "<username>", | |
359 | "perm" : "(None|repository.(read|write|admin))", |
|
391 | "perm" : "(None|repository.(read|write|admin))", | |
360 | } |
|
392 | } | |
361 |
|
393 |
@@ -29,6 +29,8 b' news' | |||||
29 | - diff configuration, toggle white lines and context lines |
|
29 | - diff configuration, toggle white lines and context lines | |
30 | - #307 configurable diffs, whitespace toggle, increasing context lines |
|
30 | - #307 configurable diffs, whitespace toggle, increasing context lines | |
31 | - sorting on branches, tags and bookmarks using YUI datatable |
|
31 | - sorting on branches, tags and bookmarks using YUI datatable | |
|
32 | - improved file filter on files page | |||
|
33 | - implements #330 api method for listing nodes ar particular revision | |||
32 |
|
34 | |||
33 | fixes |
|
35 | fixes | |
34 | ----- |
|
36 | ----- | |
@@ -36,6 +38,7 b' fixes' | |||||
36 | - rewrote dbsession management for atomic operations, and better error handling |
|
38 | - rewrote dbsession management for atomic operations, and better error handling | |
37 | - fixed sorting of repo tables |
|
39 | - fixed sorting of repo tables | |
38 | - #326 escape of special html entities in diffs |
|
40 | - #326 escape of special html entities in diffs | |
|
41 | - normalized user_name => username in api attributes | |||
39 |
|
42 | |||
40 | 1.2.3 (**2011-11-02**) |
|
43 | 1.2.3 (**2011-11-02**) | |
41 | ====================== |
|
44 | ====================== |
@@ -212,13 +212,13 b' class ApiController(JSONRPCController):' | |||||
212 | raise JSONRPCError('failed to create group %s' % name) |
|
212 | raise JSONRPCError('failed to create group %s' % name) | |
213 |
|
213 | |||
214 | @HasPermissionAllDecorator('hg.admin') |
|
214 | @HasPermissionAllDecorator('hg.admin') | |
215 |
def add_user_to_users_group(self, apiuser, group_name, user |
|
215 | def add_user_to_users_group(self, apiuser, group_name, username): | |
216 | """" |
|
216 | """" | |
217 | Add a user to a group |
|
217 | Add a user to a group | |
218 |
|
218 | |||
219 | :param apiuser: |
|
219 | :param apiuser: | |
220 | :param group_name: |
|
220 | :param group_name: | |
221 |
:param user |
|
221 | :param username: | |
222 | """ |
|
222 | """ | |
223 |
|
223 | |||
224 | try: |
|
224 | try: | |
@@ -227,9 +227,9 b' class ApiController(JSONRPCController):' | |||||
227 | raise JSONRPCError('unknown users group %s' % group_name) |
|
227 | raise JSONRPCError('unknown users group %s' % group_name) | |
228 |
|
228 | |||
229 | try: |
|
229 | try: | |
230 |
user = User.get_by_username(user |
|
230 | user = User.get_by_username(username) | |
231 | except NoResultFound: |
|
231 | except NoResultFound: | |
232 |
raise JSONRPCError('unknown user %s' % user |
|
232 | raise JSONRPCError('unknown user %s' % username) | |
233 |
|
233 | |||
234 | ugm = UsersGroupModel().add_user_to_group(users_group, user) |
|
234 | ugm = UsersGroupModel().add_user_to_group(users_group, user) | |
235 | Session.commit() |
|
235 | Session.commit() | |
@@ -311,6 +311,34 b' class ApiController(JSONRPCController):' | |||||
311 | ) |
|
311 | ) | |
312 | return result |
|
312 | return result | |
313 |
|
313 | |||
|
314 | @HasPermissionAnyDecorator('hg.admin') | |||
|
315 | def get_repo_nodes(self, apiuser, repo_name, revision, root_path, | |||
|
316 | ret_type='all'): | |||
|
317 | """ | |||
|
318 | returns a list of nodes and it's children | |||
|
319 | for a given path at given revision. It's possible to specify ret_type | |||
|
320 | to show only files or dirs | |||
|
321 | ||||
|
322 | :param apiuser: | |||
|
323 | :param repo_name: name of repository | |||
|
324 | :param revision: revision for which listing should be done | |||
|
325 | :param root_path: path from which start displaying | |||
|
326 | :param ret_type: return type 'all|files|dirs' nodes | |||
|
327 | """ | |||
|
328 | try: | |||
|
329 | _d, _f = ScmModel().get_nodes(repo_name, revision, root_path, | |||
|
330 | flat=False) | |||
|
331 | _map = { | |||
|
332 | 'all': _d + _f, | |||
|
333 | 'files': _f, | |||
|
334 | 'dirs': _d, | |||
|
335 | } | |||
|
336 | return _map[ret_type] | |||
|
337 | except KeyError: | |||
|
338 | raise JSONRPCError('ret_type must be one of %s' % _map.keys()) | |||
|
339 | except Exception, e: | |||
|
340 | raise JSONRPCError(e) | |||
|
341 | ||||
314 | @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository') |
|
342 | @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository') | |
315 | def create_repo(self, apiuser, name, owner_name, description='', |
|
343 | def create_repo(self, apiuser, name, owner_name, description='', | |
316 | repo_type='hg', private=False): |
|
344 | repo_type='hg', private=False): | |
@@ -368,13 +396,13 b' class ApiController(JSONRPCController):' | |||||
368 | raise JSONRPCError('failed to create repository %s' % name) |
|
396 | raise JSONRPCError('failed to create repository %s' % name) | |
369 |
|
397 | |||
370 | @HasPermissionAnyDecorator('hg.admin') |
|
398 | @HasPermissionAnyDecorator('hg.admin') | |
371 |
def add_user_to_repo(self, apiuser, repo_name, user |
|
399 | def add_user_to_repo(self, apiuser, repo_name, username, perm): | |
372 | """ |
|
400 | """ | |
373 | Add permission for a user to a repository |
|
401 | Add permission for a user to a repository | |
374 |
|
402 | |||
375 | :param apiuser: |
|
403 | :param apiuser: | |
376 | :param repo_name: |
|
404 | :param repo_name: | |
377 |
:param user |
|
405 | :param username: | |
378 | :param perm: |
|
406 | :param perm: | |
379 | """ |
|
407 | """ | |
380 |
|
408 | |||
@@ -384,7 +412,7 b' class ApiController(JSONRPCController):' | |||||
384 | raise JSONRPCError('unknown repository %s' % repo) |
|
412 | raise JSONRPCError('unknown repository %s' % repo) | |
385 |
|
413 | |||
386 | try: |
|
414 | try: | |
387 |
user = User.get_by_username(user |
|
415 | user = User.get_by_username(username) | |
388 | except NoResultFound: |
|
416 | except NoResultFound: | |
389 | raise JSONRPCError('unknown user %s' % user) |
|
417 | raise JSONRPCError('unknown user %s' % user) | |
390 |
|
418 | |||
@@ -394,14 +422,14 b' class ApiController(JSONRPCController):' | |||||
394 |
|
422 | |||
395 | return dict( |
|
423 | return dict( | |
396 | msg='Added perm: %s for %s in repo: %s' % ( |
|
424 | msg='Added perm: %s for %s in repo: %s' % ( | |
397 |
perm, user |
|
425 | perm, username, repo_name | |
398 | ) |
|
426 | ) | |
399 | ) |
|
427 | ) | |
400 | except Exception: |
|
428 | except Exception: | |
401 | log.error(traceback.format_exc()) |
|
429 | log.error(traceback.format_exc()) | |
402 | raise JSONRPCError( |
|
430 | raise JSONRPCError( | |
403 | 'failed to edit permission %(repo)s for %(user)s' % dict( |
|
431 | 'failed to edit permission %(repo)s for %(user)s' % dict( | |
404 |
user=user |
|
432 | user=username, repo=repo_name | |
405 | ) |
|
433 | ) | |
406 | ) |
|
434 | ) | |
407 |
|
435 |
@@ -49,6 +49,7 b' from rhodecode.model.repo import RepoMod' | |||||
49 | from rhodecode.controllers.changeset import anchor_url, _ignorews_url,\ |
|
49 | from rhodecode.controllers.changeset import anchor_url, _ignorews_url,\ | |
50 | _context_url, get_line_ctx, get_ignore_ws |
|
50 | _context_url, get_line_ctx, get_ignore_ws | |
51 | from rhodecode.lib.diffs import wrapped_diff |
|
51 | from rhodecode.lib.diffs import wrapped_diff | |
|
52 | from rhodecode.model.scm import ScmModel | |||
52 |
|
53 | |||
53 | log = logging.getLogger(__name__) |
|
54 | log = logging.getLogger(__name__) | |
54 |
|
55 | |||
@@ -107,25 +108,6 b' class FilesController(BaseRepoController' | |||||
107 |
|
108 | |||
108 | return file_node |
|
109 | return file_node | |
109 |
|
110 | |||
110 | def __get_paths(self, changeset, starting_path): |
|
|||
111 | """recursive walk in root dir and return a set of all path in that dir |
|
|||
112 | based on repository walk function |
|
|||
113 | """ |
|
|||
114 | _files = list() |
|
|||
115 | _dirs = list() |
|
|||
116 |
|
||||
117 | try: |
|
|||
118 | tip = changeset |
|
|||
119 | for topnode, dirs, files in tip.walk(starting_path): |
|
|||
120 | for f in files: |
|
|||
121 | _files.append(f.path) |
|
|||
122 | for d in dirs: |
|
|||
123 | _dirs.append(d.path) |
|
|||
124 | except RepositoryError, e: |
|
|||
125 | log.debug(traceback.format_exc()) |
|
|||
126 | pass |
|
|||
127 | return _dirs, _files |
|
|||
128 |
|
||||
129 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
111 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', | |
130 | 'repository.admin') |
|
112 | 'repository.admin') | |
131 | def index(self, repo_name, revision, f_path): |
|
113 | def index(self, repo_name, revision, f_path): | |
@@ -505,6 +487,7 b' class FilesController(BaseRepoController' | |||||
505 | def nodelist(self, repo_name, revision, f_path): |
|
487 | def nodelist(self, repo_name, revision, f_path): | |
506 | if request.environ.get('HTTP_X_PARTIAL_XHR'): |
|
488 | if request.environ.get('HTTP_X_PARTIAL_XHR'): | |
507 | cs = self.__get_cs_or_redirect(revision, repo_name) |
|
489 | cs = self.__get_cs_or_redirect(revision, repo_name) | |
508 |
_d, _f = |
|
490 | _d, _f = ScmModel().get_nodes(repo_name, cs.raw_id, f_path, | |
|
491 | flat=False) | |||
509 | return _d + _f |
|
492 | return _d + _f | |
510 |
|
493 |
@@ -129,6 +129,7 b' class MakeIndex(BasePasterCommand):' | |||||
129 | " destroy old and build from scratch", |
|
129 | " destroy old and build from scratch", | |
130 | default=False) |
|
130 | default=False) | |
131 |
|
131 | |||
|
132 | ||||
132 | class ResultWrapper(object): |
|
133 | class ResultWrapper(object): | |
133 | def __init__(self, search_type, searcher, matcher, highlight_items): |
|
134 | def __init__(self, search_type, searcher, matcher, highlight_items): | |
134 | self.search_type = search_type |
|
135 | self.search_type = search_type | |
@@ -176,7 +177,6 b' class ResultWrapper(object):' | |||||
176 | slice.append(self.get_full_content(docid)) |
|
177 | slice.append(self.get_full_content(docid)) | |
177 | return slice |
|
178 | return slice | |
178 |
|
179 | |||
179 |
|
||||
180 | def get_full_content(self, docid): |
|
180 | def get_full_content(self, docid): | |
181 | res = self.searcher.stored_fields(docid[0]) |
|
181 | res = self.searcher.stored_fields(docid[0]) | |
182 | f_path = res['path'][res['path'].find(res['repository']) \ |
|
182 | f_path = res['path'][res['path'].find(res['repository']) \ | |
@@ -198,7 +198,7 b' class ResultWrapper(object):' | |||||
198 | Smart function that implements chunking the content |
|
198 | Smart function that implements chunking the content | |
199 | but not overlap chunks so it doesn't highlight the same |
|
199 | but not overlap chunks so it doesn't highlight the same | |
200 | close occurrences twice. |
|
200 | close occurrences twice. | |
201 |
|
201 | |||
202 | :param matcher: |
|
202 | :param matcher: | |
203 | :param size: |
|
203 | :param size: | |
204 | """ |
|
204 | """ |
@@ -391,5 +391,35 b' class ScmModel(BaseModel):' | |||||
391 |
|
391 | |||
392 | self.mark_for_invalidation(repo_name) |
|
392 | self.mark_for_invalidation(repo_name) | |
393 |
|
393 | |||
|
394 | def get_nodes(self, repo_name, revision, root_path='/', flat=True): | |||
|
395 | """ | |||
|
396 | recursive walk in root dir and return a set of all path in that dir | |||
|
397 | based on repository walk function | |||
|
398 | ||||
|
399 | :param repo_name: name of repository | |||
|
400 | :param revision: revision for which to list nodes | |||
|
401 | :param root_path: root path to list | |||
|
402 | :param flat: return as a list, if False returns a dict with decription | |||
|
403 | ||||
|
404 | """ | |||
|
405 | _files = list() | |||
|
406 | _dirs = list() | |||
|
407 | try: | |||
|
408 | _repo = self.__get_repo(repo_name) | |||
|
409 | changeset = _repo.scm_instance.get_changeset(revision) | |||
|
410 | root_path = root_path.lstrip('/') | |||
|
411 | for topnode, dirs, files in changeset.walk(root_path): | |||
|
412 | for f in files: | |||
|
413 | _files.append(f.path if flat else {"name": f.path, | |||
|
414 | "type": "file"}) | |||
|
415 | for d in dirs: | |||
|
416 | _dirs.append(d.path if flat else {"name": d.path, | |||
|
417 | "type": "dir"}) | |||
|
418 | except RepositoryError: | |||
|
419 | log.debug(traceback.format_exc()) | |||
|
420 | raise | |||
|
421 | ||||
|
422 | return _dirs, _files | |||
|
423 | ||||
394 | def get_unread_journal(self): |
|
424 | def get_unread_journal(self): | |
395 | return self.sa.query(UserLog).count() |
|
425 | return self.sa.query(UserLog).count() |
@@ -418,6 +418,11 b' var fileBrowserListeners = function(curr' | |||||
418 | nodes = JSON.parse(o.responseText); |
|
418 | nodes = JSON.parse(o.responseText); | |
419 | YUD.setStyle('node_filter_box_loading','display','none'); |
|
419 | YUD.setStyle('node_filter_box_loading','display','none'); | |
420 | YUD.setStyle('node_filter_box','display',''); |
|
420 | YUD.setStyle('node_filter_box','display',''); | |
|
421 | n_filter.focus(); | |||
|
422 | if(YUD.hasClass(n_filter,'init')){ | |||
|
423 | n_filter.value = ''; | |||
|
424 | YUD.removeClass(n_filter,'init'); | |||
|
425 | } | |||
421 | }, |
|
426 | }, | |
422 | failure:function(o){ |
|
427 | failure:function(o){ | |
423 | console.log('failed to load'); |
|
428 | console.log('failed to load'); | |
@@ -430,13 +435,14 b' var fileBrowserListeners = function(curr' | |||||
430 | return function(){ |
|
435 | return function(){ | |
431 | // Reset timeout |
|
436 | // Reset timeout | |
432 | F.filterTimeout = null; |
|
437 | F.filterTimeout = null; | |
433 | var query = e.target.value; |
|
438 | var query = e.target.value.toLowerCase(); | |
434 | var match = []; |
|
439 | var match = []; | |
435 | var matches = 0; |
|
440 | var matches = 0; | |
436 | var matches_max = 20; |
|
441 | var matches_max = 20; | |
437 | if (query != ""){ |
|
442 | if (query != ""){ | |
438 | for(var i=0;i<nodes.length;i++){ |
|
443 | for(var i=0;i<nodes.length;i++){ | |
439 | var pos = nodes[i].toLowerCase().indexOf(query) |
|
444 | ||
|
445 | var pos = nodes[i].name.toLowerCase().indexOf(query) | |||
440 | if(query && pos != -1){ |
|
446 | if(query && pos != -1){ | |
441 |
|
447 | |||
442 | matches++ |
|
448 | matches++ | |
@@ -445,11 +451,12 b' var fileBrowserListeners = function(curr' | |||||
445 | break; |
|
451 | break; | |
446 | } |
|
452 | } | |
447 |
|
453 | |||
448 | var n = nodes[i]; |
|
454 | var n = nodes[i].name; | |
|
455 | var t = nodes[i].type; | |||
449 | var n_hl = n.substring(0,pos) |
|
456 | var n_hl = n.substring(0,pos) | |
450 | +"<b>{0}</b>".format(n.substring(pos,pos+query.length)) |
|
457 | +"<b>{0}</b>".format(n.substring(pos,pos+query.length)) | |
451 | +n.substring(pos+query.length) |
|
458 | +n.substring(pos+query.length) | |
452 |
match.push('<tr><td><a class="browser- |
|
459 | match.push('<tr><td><a class="browser-{0}" href="{1}">{2}</a></td><td colspan="5"></td></tr>'.format(t,node_url.replace('__FPATH__',n),n_hl)); | |
453 | } |
|
460 | } | |
454 | if(match.length >= matches_max){ |
|
461 | if(match.length >= matches_max){ | |
455 | match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(truncated_lbl)); |
|
462 | match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(truncated_lbl)); | |
@@ -479,7 +486,10 b' var fileBrowserListeners = function(curr' | |||||
479 | F.initFilter(); |
|
486 | F.initFilter(); | |
480 | }) |
|
487 | }) | |
481 | YUE.on(n_filter,'click',function(){ |
|
488 | YUE.on(n_filter,'click',function(){ | |
482 | n_filter.value = ''; |
|
489 | if(YUD.hasClass(n_filter,'init')){ | |
|
490 | n_filter.value = ''; | |||
|
491 | YUD.removeClass(n_filter,'init'); | |||
|
492 | } | |||
483 | }); |
|
493 | }); | |
484 | YUE.on(n_filter,'keyup',function(e){ |
|
494 | YUE.on(n_filter,'keyup',function(e){ | |
485 | clearTimeout(F.filterTimeout); |
|
495 | clearTimeout(F.filterTimeout); |
@@ -34,7 +34,7 b'' | |||||
34 | <div> |
|
34 | <div> | |
35 | <div id="node_filter_box_loading" style="display:none">${_('Loading file list...')}</div> |
|
35 | <div id="node_filter_box_loading" style="display:none">${_('Loading file list...')}</div> | |
36 | <div id="node_filter_box" style="display:none"> |
|
36 | <div id="node_filter_box" style="display:none"> | |
37 | ${h.files_breadcrumbs(c.repo_name,c.changeset.raw_id,c.file.path)}/<input type="text" value="type to search..." name="filter" size="25" id="node_filter" autocomplete="off"> |
|
37 | ${h.files_breadcrumbs(c.repo_name,c.changeset.raw_id,c.file.path)}/<input class="init" type="text" value="type to search..." name="filter" size="25" id="node_filter" autocomplete="off"> | |
38 | </div> |
|
38 | </div> | |
39 | </div> |
|
39 | </div> | |
40 | </div> |
|
40 | </div> |
General Comments 0
You need to be logged in to leave comments.
Login now