Changeset - 25dbbdae3ed9
[Not reviewed]
beta
0 8 0
Marcin Kuzminski - 13 years ago 2013-04-08 23:49:08
marcin@python-works.com
consistent handling of grant/revoke of permissions widgets
- use 1 method for users and for users groups
- use common JS for ajax revoke
- cleanup code
8 files changed with 103 insertions and 150 deletions:
0 comments (0 inline, 0 general)
rhodecode/config/routing.py
Show inline comments
 
"""
 
Routes configuration
 

	
 
The more specific and detailed routes should be defined first so they
 
may take precedent over the more generic routes. For more information
 
refer to the routes manual at http://routes.groovie.org/docs/
 
"""
 
from __future__ import with_statement
 
from routes import Mapper
 

	
 
# prefix for non repository related links needs to be prefixed with `/`
 
ADMIN_PREFIX = '/_admin'
 

	
 

	
 
def make_map(config):
 
    """Create, configure and return the routes Mapper"""
 
    rmap = Mapper(directory=config['pylons.paths']['controllers'],
 
                 always_scan=config['debug'])
 
    rmap.minimization = False
 
    rmap.explicit = False
 

	
 
    from rhodecode.lib.utils import is_valid_repo
 
    from rhodecode.lib.utils import is_valid_repos_group
 

	
 
    def check_repo(environ, match_dict):
 
        """
 
        check for valid repository for proper 404 handling
 

	
 
        :param environ:
 
        :param match_dict:
 
        """
 
        from rhodecode.model.db import Repository
 
        repo_name = match_dict.get('repo_name')
 

	
 
        if match_dict.get('f_path'):
 
            #fix for multiple initial slashes that causes errors
 
            match_dict['f_path'] = match_dict['f_path'].lstrip('/')
 

	
 
        try:
 
            by_id = repo_name.split('_')
 
            if len(by_id) == 2 and by_id[1].isdigit() and by_id[0] == '':
 
                repo_name = Repository.get(by_id[1]).repo_name
 
                match_dict['repo_name'] = repo_name
 
        except Exception:
 
            pass
 

	
 
        return is_valid_repo(repo_name, config['base_path'])
 

	
 
    def check_group(environ, match_dict):
 
        """
 
        check for valid repository group for proper 404 handling
 

	
 
        :param environ:
 
        :param match_dict:
 
        """
 
        repos_group_name = match_dict.get('group_name')
 
        return is_valid_repos_group(repos_group_name, config['base_path'])
 

	
 
    def check_group_skip_path(environ, match_dict):
 
        """
 
        check for valid repository group for proper 404 handling, but skips
 
        verification of existing path
 

	
 
        :param environ:
 
        :param match_dict:
 
        """
 
        repos_group_name = match_dict.get('group_name')
 
        return is_valid_repos_group(repos_group_name, config['base_path'],
 
                                    skip_path_check=True)
 

	
 
    def check_int(environ, match_dict):
 
        return match_dict.get('id').isdigit()
 

	
 
    # The ErrorController route (handles 404/500 error pages); it should
 
    # likely stay at the top, ensuring it can always be resolved
 
    rmap.connect('/error/{action}', controller='error')
 
    rmap.connect('/error/{action}/{id}', controller='error')
 

	
 
    #==========================================================================
 
    # CUSTOM ROUTES HERE
 
    #==========================================================================
 

	
 
    #MAIN PAGE
 
    rmap.connect('home', '/', controller='home', action='index')
 
    rmap.connect('repo_switcher', '/repos', controller='home',
 
                 action='repo_switcher')
 
    rmap.connect('branch_tag_switcher', '/branches-tags/{repo_name:.*?}',
 
                 controller='home', action='branch_tag_switcher')
 
    rmap.connect('bugtracker',
 
                 "http://bitbucket.org/marcinkuzminski/rhodecode/issues",
 
                 _static=True)
 
    rmap.connect('rst_help',
 
                 "http://docutils.sourceforge.net/docs/user/rst/quickref.html",
 
                 _static=True)
 
    rmap.connect('rhodecode_official', "http://rhodecode.org", _static=True)
 

	
 
    #ADMIN REPOSITORY REST ROUTES
 
    with rmap.submapper(path_prefix=ADMIN_PREFIX,
 
                        controller='admin/repos') as m:
 
        m.connect("repos", "/repos",
 
             action="create", conditions=dict(method=["POST"]))
 
        m.connect("repos", "/repos",
 
             action="index", conditions=dict(method=["GET"]))
 
        m.connect("formatted_repos", "/repos.{format}",
 
             action="index",
 
            conditions=dict(method=["GET"]))
 
        m.connect("new_repo", "/create_repository",
 
                  action="create_repository", conditions=dict(method=["GET"]))
 
        m.connect("/repos/{repo_name:.*?}",
 
             action="update", conditions=dict(method=["PUT"],
 
                                              function=check_repo))
 
        m.connect("/repos/{repo_name:.*?}",
 
             action="delete", conditions=dict(method=["DELETE"],
 
                                              function=check_repo))
 
        m.connect("formatted_edit_repo", "/repos/{repo_name:.*?}.{format}/edit",
 
             action="edit", conditions=dict(method=["GET"],
 
                                            function=check_repo))
 
        m.connect("repo", "/repos/{repo_name:.*?}",
 
             action="show", conditions=dict(method=["GET"],
 
                                            function=check_repo))
 
        m.connect("formatted_repo", "/repos/{repo_name:.*?}.{format}",
 
             action="show", conditions=dict(method=["GET"],
 
                                            function=check_repo))
 
        #add repo perm member
 
        m.connect('set_repo_perm_member', "/set_repo_perm_member/{repo_name:.*?}",
 
        m.connect('set_repo_perm_member',
 
                  "/repos/{repo_name:.*?}/grant_perm",
 
             action="set_repo_perm_member",
 
             conditions=dict(method=["POST"], function=check_repo))
 

	
 
        #ajax delete repo perm user
 
        m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*?}",
 
             action="delete_perm_user",
 
             conditions=dict(method=["DELETE"], function=check_repo))
 

	
 
        #ajax delete repo perm users_group
 
        m.connect('delete_repo_users_group',
 
                  "/repos_delete_users_group/{repo_name:.*?}",
 
                  action="delete_perm_users_group",
 
        m.connect('delete_repo_perm_member',
 
                  "/repos/{repo_name:.*?}/revoke_perm",
 
                  action="delete_repo_perm_member",
 
                  conditions=dict(method=["DELETE"], function=check_repo))
 

	
 
        #settings actions
 
        m.connect('repo_stats', "/repos_stats/{repo_name:.*?}",
 
                  action="repo_stats", conditions=dict(method=["DELETE"],
 
                                                       function=check_repo))
 
        m.connect('repo_cache', "/repos_cache/{repo_name:.*?}",
 
                  action="repo_cache", conditions=dict(method=["DELETE"],
 
                                                       function=check_repo))
 
        m.connect('repo_public_journal', "/repos_public_journal/{repo_name:.*?}",
 
                  action="repo_public_journal", conditions=dict(method=["PUT"],
 
                                                        function=check_repo))
 
        m.connect('repo_pull', "/repo_pull/{repo_name:.*?}",
 
                  action="repo_pull", conditions=dict(method=["PUT"],
 
                                                      function=check_repo))
 
        m.connect('repo_as_fork', "/repo_as_fork/{repo_name:.*?}",
 
                  action="repo_as_fork", conditions=dict(method=["PUT"],
 
                                                      function=check_repo))
 
        m.connect('repo_locking', "/repo_locking/{repo_name:.*?}",
 
                  action="repo_locking", conditions=dict(method=["PUT"],
 
                                                      function=check_repo))
 
        m.connect('toggle_locking', "/locking_toggle/{repo_name:.*?}",
 
                  action="toggle_locking", conditions=dict(method=["GET"],
 
                                                      function=check_repo))
 

	
 
        #repo fields
 
        m.connect('create_repo_fields', "/repo_fields/{repo_name:.*?}/new",
 
                  action="create_repo_field", conditions=dict(method=["PUT"],
 
                                                      function=check_repo))
 

	
 
        m.connect('delete_repo_fields', "/repo_fields/{repo_name:.*?}/{field_id}",
 
                  action="delete_repo_field", conditions=dict(method=["DELETE"],
 
                                                      function=check_repo))
 

	
 
    with rmap.submapper(path_prefix=ADMIN_PREFIX,
 
                        controller='admin/repos_groups') as m:
 
        m.connect("repos_groups", "/repos_groups",
 
                  action="create", conditions=dict(method=["POST"]))
 
        m.connect("repos_groups", "/repos_groups",
 
                  action="index", conditions=dict(method=["GET"]))
 
        m.connect("formatted_repos_groups", "/repos_groups.{format}",
 
                  action="index", conditions=dict(method=["GET"]))
 
        m.connect("new_repos_group", "/repos_groups/new",
 
                  action="new", conditions=dict(method=["GET"]))
 
        m.connect("formatted_new_repos_group", "/repos_groups/new.{format}",
 
                  action="new", conditions=dict(method=["GET"]))
 
        m.connect("update_repos_group", "/repos_groups/{group_name:.*?}",
 
                  action="update", conditions=dict(method=["PUT"],
 
                                                   function=check_group))
 
        #add repo group perm member
 
        m.connect('set_repo_group_perm_member',
 
                  "/repos_groups/{group_name:.*?}/grant_perm",
 
                  action="set_repo_group_perm_member",
 
                  conditions=dict(method=["POST"], function=check_group))
 

	
 
        #ajax delete repo group perm
 
        m.connect('delete_repo_group_perm_member',
 
                  "/repos_groups/{group_name:.*?}/revoke_perm",
 
                  action="delete_repo_group_perm_member",
 
                  conditions=dict(method=["DELETE"], function=check_group))
 

	
 
        m.connect("delete_repos_group", "/repos_groups/{group_name:.*?}",
 
                  action="delete", conditions=dict(method=["DELETE"],
 
                                                   function=check_group_skip_path))
 
        m.connect("edit_repos_group", "/repos_groups/{group_name:.*?}/edit",
 
                  action="edit", conditions=dict(method=["GET"],
 
                                                 function=check_group))
 
        m.connect("formatted_edit_repos_group",
 
                  "/repos_groups/{group_name:.*?}.{format}/edit",
 
                  action="edit", conditions=dict(method=["GET"],
 
                                                 function=check_group))
 
        m.connect("repos_group", "/repos_groups/{group_name:.*?}",
 
                  action="show", conditions=dict(method=["GET"],
 
                                                 function=check_group))
 
        m.connect("formatted_repos_group", "/repos_groups/{group_name:.*?}.{format}",
 
                  action="show", conditions=dict(method=["GET"],
 
                                                 function=check_group))
 

	
 
        #add repo perm member
 
        m.connect('set_repo_group_perm_member',
 
                  "/set_repo_group_perm_member/{group_name:.*?}",
 
             action="set_repo_group_perm_member",
 
             conditions=dict(method=["POST"], function=check_group))
 

	
 
        # ajax delete repository group perm user
 
        m.connect('delete_repos_group_user_perm',
 
                  "/delete_repos_group_user_perm/{group_name:.*?}",
 
             action="delete_repos_group_user_perm",
 
             conditions=dict(method=["DELETE"], function=check_group))
 

	
 
        # ajax delete repository group perm users_group
 
        m.connect('delete_repos_group_users_group_perm',
 
                  "/delete_repos_group_users_group_perm/{group_name:.*?}",
 
                  action="delete_repos_group_users_group_perm",
 
                  conditions=dict(method=["DELETE"], function=check_group))
 

	
 
    #ADMIN USER REST ROUTES
 
    with rmap.submapper(path_prefix=ADMIN_PREFIX,
 
                        controller='admin/users') as m:
 
        m.connect("users", "/users",
 
                  action="create", conditions=dict(method=["POST"]))
 
        m.connect("users", "/users",
 
                  action="index", conditions=dict(method=["GET"]))
 
        m.connect("formatted_users", "/users.{format}",
 
                  action="index", conditions=dict(method=["GET"]))
 
        m.connect("new_user", "/users/new",
 
                  action="new", conditions=dict(method=["GET"]))
 
        m.connect("formatted_new_user", "/users/new.{format}",
 
                  action="new", conditions=dict(method=["GET"]))
 
        m.connect("update_user", "/users/{id}",
 
                  action="update", conditions=dict(method=["PUT"]))
 
        m.connect("delete_user", "/users/{id}",
 
                  action="delete", conditions=dict(method=["DELETE"]))
 
        m.connect("edit_user", "/users/{id}/edit",
 
                  action="edit", conditions=dict(method=["GET"]))
 
        m.connect("formatted_edit_user",
 
                  "/users/{id}.{format}/edit",
 
                  action="edit", conditions=dict(method=["GET"]))
 
        m.connect("user", "/users/{id}",
 
                  action="show", conditions=dict(method=["GET"]))
 
        m.connect("formatted_user", "/users/{id}.{format}",
 
                  action="show", conditions=dict(method=["GET"]))
 

	
 
        #EXTRAS USER ROUTES
 
        m.connect("user_perm", "/users_perm/{id}",
 
                  action="update_perm", conditions=dict(method=["PUT"]))
 
        m.connect("user_emails", "/users_emails/{id}",
 
                  action="add_email", conditions=dict(method=["PUT"]))
 
        m.connect("user_emails_delete", "/users_emails/{id}",
 
                  action="delete_email", conditions=dict(method=["DELETE"]))
 
        m.connect("user_ips", "/users_ips/{id}",
 
                  action="add_ip", conditions=dict(method=["PUT"]))
 
        m.connect("user_ips_delete", "/users_ips/{id}",
 
                  action="delete_ip", conditions=dict(method=["DELETE"]))
 

	
 
    #ADMIN USER GROUPS REST ROUTES
 
    with rmap.submapper(path_prefix=ADMIN_PREFIX,
 
                        controller='admin/users_groups') as m:
 
        m.connect("users_groups", "/users_groups",
 
                  action="create", conditions=dict(method=["POST"]))
 
        m.connect("users_groups", "/users_groups",
 
                  action="index", conditions=dict(method=["GET"]))
 
        m.connect("formatted_users_groups", "/users_groups.{format}",
 
                  action="index", conditions=dict(method=["GET"]))
 
        m.connect("new_users_group", "/users_groups/new",
 
                  action="new", conditions=dict(method=["GET"]))
 
        m.connect("formatted_new_users_group", "/users_groups/new.{format}",
 
                  action="new", conditions=dict(method=["GET"]))
 
        m.connect("update_users_group", "/users_groups/{id}",
 
                  action="update", conditions=dict(method=["PUT"]))
 
        m.connect("delete_users_group", "/users_groups/{id}",
 
                  action="delete", conditions=dict(method=["DELETE"]))
 
        m.connect("edit_users_group", "/users_groups/{id}/edit",
 
                  action="edit", conditions=dict(method=["GET"]))
 
        m.connect("formatted_edit_users_group",
 
                  "/users_groups/{id}.{format}/edit",
 
                  action="edit", conditions=dict(method=["GET"]))
 
        m.connect("users_group", "/users_groups/{id}",
 
                  action="show", conditions=dict(method=["GET"]))
 
        m.connect("formatted_users_group", "/users_groups/{id}.{format}",
 
                  action="show", conditions=dict(method=["GET"]))
 

	
 
        #EXTRAS USER ROUTES
 
        # update
 
        m.connect("users_group_perm", "/users_groups/{id}/update_global_perm",
 
                  action="update_perm", conditions=dict(method=["PUT"]))
 

	
 
        #add user group perm member
 
        m.connect('set_user_group_perm_member', "/users_groups/{id}/grant_perm",
 
             action="set_user_group_perm_member",
 
             conditions=dict(method=["POST"]))
 

	
 
        #ajax delete user group perm
 
        m.connect('delete_user_group_perm_member', "/users_groups/{id}/revoke_perm",
 
             action="delete_user_group_perm_member",
 
             conditions=dict(method=["DELETE"]))
 

	
 
    #ADMIN GROUP REST ROUTES
 
    rmap.resource('group', 'groups',
 
                  controller='admin/groups', path_prefix=ADMIN_PREFIX)
 

	
 
    #ADMIN PERMISSIONS REST ROUTES
 
    rmap.resource('permission', 'permissions',
 
                  controller='admin/permissions', path_prefix=ADMIN_PREFIX)
 

	
 
    #ADMIN DEFAULTS REST ROUTES
 
    rmap.resource('default', 'defaults',
 
                  controller='admin/defaults', path_prefix=ADMIN_PREFIX)
 

	
 
    ##ADMIN LDAP SETTINGS
 
    rmap.connect('ldap_settings', '%s/ldap' % ADMIN_PREFIX,
 
                 controller='admin/ldap_settings', action='ldap_settings',
 
                 conditions=dict(method=["POST"]))
 

	
 
    rmap.connect('ldap_home', '%s/ldap' % ADMIN_PREFIX,
 
                 controller='admin/ldap_settings')
 

	
 
    #ADMIN SETTINGS REST ROUTES
 
    with rmap.submapper(path_prefix=ADMIN_PREFIX,
 
                        controller='admin/settings') as m:
 
        m.connect("admin_settings", "/settings",
 
                  action="create", conditions=dict(method=["POST"]))
 
        m.connect("admin_settings", "/settings",
 
                  action="index", conditions=dict(method=["GET"]))
 
        m.connect("formatted_admin_settings", "/settings.{format}",
 
                  action="index", conditions=dict(method=["GET"]))
 
        m.connect("admin_new_setting", "/settings/new",
 
                  action="new", conditions=dict(method=["GET"]))
 
        m.connect("formatted_admin_new_setting", "/settings/new.{format}",
 
                  action="new", conditions=dict(method=["GET"]))
 
        m.connect("/settings/{setting_id}",
 
                  action="update", conditions=dict(method=["PUT"]))
 
        m.connect("/settings/{setting_id}",
 
                  action="delete", conditions=dict(method=["DELETE"]))
 
        m.connect("admin_edit_setting", "/settings/{setting_id}/edit",
 
                  action="edit", conditions=dict(method=["GET"]))
 
        m.connect("formatted_admin_edit_setting",
 
                  "/settings/{setting_id}.{format}/edit",
 
                  action="edit", conditions=dict(method=["GET"]))
 
        m.connect("admin_setting", "/settings/{setting_id}",
 
                  action="show", conditions=dict(method=["GET"]))
 
        m.connect("formatted_admin_setting", "/settings/{setting_id}.{format}",
 
                  action="show", conditions=dict(method=["GET"]))
 
        m.connect("admin_settings_my_account", "/my_account",
 
                  action="my_account", conditions=dict(method=["GET"]))
 
        m.connect("admin_settings_my_account_update", "/my_account_update",
 
                  action="my_account_update", conditions=dict(method=["PUT"]))
 
        m.connect("admin_settings_my_repos", "/my_account/repos",
 
                  action="my_account_my_repos", conditions=dict(method=["GET"]))
 
        m.connect("admin_settings_my_pullrequests", "/my_account/pull_requests",
 
                  action="my_account_my_pullrequests", conditions=dict(method=["GET"]))
 

	
 
    #NOTIFICATION REST ROUTES
 
    with rmap.submapper(path_prefix=ADMIN_PREFIX,
 
                        controller='admin/notifications') as m:
 
        m.connect("notifications", "/notifications",
 
                  action="create", conditions=dict(method=["POST"]))
 
        m.connect("notifications", "/notifications",
 
                  action="index", conditions=dict(method=["GET"]))
 
        m.connect("notifications_mark_all_read", "/notifications/mark_all_read",
 
                  action="mark_all_read", conditions=dict(method=["GET"]))
 
        m.connect("formatted_notifications", "/notifications.{format}",
 
                  action="index", conditions=dict(method=["GET"]))
 
        m.connect("new_notification", "/notifications/new",
 
                  action="new", conditions=dict(method=["GET"]))
 
        m.connect("formatted_new_notification", "/notifications/new.{format}",
 
                  action="new", conditions=dict(method=["GET"]))
 
        m.connect("/notification/{notification_id}",
 
                  action="update", conditions=dict(method=["PUT"]))
 
        m.connect("/notification/{notification_id}",
 
                  action="delete", conditions=dict(method=["DELETE"]))
 
        m.connect("edit_notification", "/notification/{notification_id}/edit",
 
                  action="edit", conditions=dict(method=["GET"]))
 
        m.connect("formatted_edit_notification",
 
                  "/notification/{notification_id}.{format}/edit",
 
                  action="edit", conditions=dict(method=["GET"]))
 
        m.connect("notification", "/notification/{notification_id}",
 
                  action="show", conditions=dict(method=["GET"]))
 
        m.connect("formatted_notification", "/notifications/{notification_id}.{format}",
 
                  action="show", conditions=dict(method=["GET"]))
 

	
 
    #ADMIN MAIN PAGES
 
    with rmap.submapper(path_prefix=ADMIN_PREFIX,
 
                        controller='admin/admin') as m:
 
        m.connect('admin_home', '', action='index')
 
        m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
 
                  action='add_repo')
 

	
 
    #==========================================================================
 
    # API V2
 
    #==========================================================================
 
    with rmap.submapper(path_prefix=ADMIN_PREFIX,
 
                        controller='api/api') as m:
 
        m.connect('api', '/api')
 

	
 
    #USER JOURNAL
 
    rmap.connect('journal', '%s/journal' % ADMIN_PREFIX,
 
                 controller='journal', action='index')
 
    rmap.connect('journal_rss', '%s/journal/rss' % ADMIN_PREFIX,
 
                 controller='journal', action='journal_rss')
 
    rmap.connect('journal_atom', '%s/journal/atom' % ADMIN_PREFIX,
 
                 controller='journal', action='journal_atom')
 

	
 
    rmap.connect('public_journal', '%s/public_journal' % ADMIN_PREFIX,
 
                 controller='journal', action="public_journal")
 

	
 
    rmap.connect('public_journal_rss', '%s/public_journal/rss' % ADMIN_PREFIX,
 
                 controller='journal', action="public_journal_rss")
 

	
 
    rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % ADMIN_PREFIX,
 
                 controller='journal', action="public_journal_rss")
 

	
 
    rmap.connect('public_journal_atom',
 
                 '%s/public_journal/atom' % ADMIN_PREFIX, controller='journal',
 
                 action="public_journal_atom")
 

	
 
    rmap.connect('public_journal_atom_old',
 
                 '%s/public_journal_atom' % ADMIN_PREFIX, controller='journal',
 
                 action="public_journal_atom")
 

	
 
    rmap.connect('toggle_following', '%s/toggle_following' % ADMIN_PREFIX,
 
                 controller='journal', action='toggle_following',
 
                 conditions=dict(method=["POST"]))
 

	
 
    #SEARCH
 
    rmap.connect('search', '%s/search' % ADMIN_PREFIX, controller='search',)
 
    rmap.connect('search_repo_admin', '%s/search/{repo_name:.*}' % ADMIN_PREFIX,
 
                 controller='search',
 
                 conditions=dict(function=check_repo))
 
    rmap.connect('search_repo', '/{repo_name:.*?}/search',
 
                 controller='search',
 
                 conditions=dict(function=check_repo),
 
                 )
 

	
 
    #LOGIN/LOGOUT/REGISTER/SIGN IN
 
    rmap.connect('login_home', '%s/login' % ADMIN_PREFIX, controller='login')
 
    rmap.connect('logout_home', '%s/logout' % ADMIN_PREFIX, controller='login',
 
                 action='logout')
 

	
 
    rmap.connect('register', '%s/register' % ADMIN_PREFIX, controller='login',
 
                 action='register')
 

	
 
    rmap.connect('reset_password', '%s/password_reset' % ADMIN_PREFIX,
 
                 controller='login', action='password_reset')
 

	
 
    rmap.connect('reset_password_confirmation',
 
                 '%s/password_reset_confirmation' % ADMIN_PREFIX,
 
                 controller='login', action='password_reset_confirmation')
 

	
 
    #FEEDS
 
    rmap.connect('rss_feed_home', '/{repo_name:.*?}/feed/rss',
 
                controller='feed', action='rss',
 
                conditions=dict(function=check_repo))
 

	
 
    rmap.connect('atom_feed_home', '/{repo_name:.*?}/feed/atom',
 
                controller='feed', action='atom',
 
                conditions=dict(function=check_repo))
 

	
 
    #==========================================================================
 
    # REPOSITORY ROUTES
 
    #==========================================================================
 
    rmap.connect('summary_home', '/{repo_name:.*?}',
 
                controller='summary',
 
                conditions=dict(function=check_repo))
 

	
 
    rmap.connect('repo_size', '/{repo_name:.*?}/repo_size',
 
                controller='summary', action='repo_size',
 
                conditions=dict(function=check_repo))
 

	
 
    rmap.connect('repos_group_home', '/{group_name:.*}',
 
                controller='admin/repos_groups', action="show_by_name",
 
                conditions=dict(function=check_group))
 

	
 
    rmap.connect('changeset_home', '/{repo_name:.*?}/changeset/{revision}',
 
                controller='changeset', revision='tip',
 
                conditions=dict(function=check_repo))
 

	
 
    # no longer user, but kept for routes to work
 
    rmap.connect("_edit_repo", "/{repo_name:.*?}/edit",
 
                 controller='admin/repos', action="edit",
 
                 conditions=dict(method=["GET"], function=check_repo)
 
                 )
 

	
 
    rmap.connect("edit_repo", "/{repo_name:.*?}/settings",
 
                 controller='admin/repos', action="edit",
 
                 conditions=dict(method=["GET"], function=check_repo)
 
                 )
 

	
 
    #still working url for backward compat.
 
    rmap.connect('raw_changeset_home_depraced',
 
                 '/{repo_name:.*?}/raw-changeset/{revision}',
 
                 controller='changeset', action='changeset_raw',
 
                 revision='tip', conditions=dict(function=check_repo))
 

	
 
    ## new URLs
 
    rmap.connect('changeset_raw_home',
 
                 '/{repo_name:.*?}/changeset-diff/{revision}',
 
                 controller='changeset', action='changeset_raw',
 
                 revision='tip', conditions=dict(function=check_repo))
 

	
 
    rmap.connect('changeset_patch_home',
 
                 '/{repo_name:.*?}/changeset-patch/{revision}',
 
                 controller='changeset', action='changeset_patch',
 
                 revision='tip', conditions=dict(function=check_repo))
 

	
 
    rmap.connect('changeset_download_home',
 
                 '/{repo_name:.*?}/changeset-download/{revision}',
 
                 controller='changeset', action='changeset_download',
 
                 revision='tip', conditions=dict(function=check_repo))
 

	
 
    rmap.connect('changeset_comment',
 
                 '/{repo_name:.*?}/changeset/{revision}/comment',
 
                controller='changeset', revision='tip', action='comment',
 
                conditions=dict(function=check_repo))
 

	
 
    rmap.connect('changeset_comment_preview',
 
                 '/{repo_name:.*?}/changeset/comment/preview',
 
                controller='changeset', action='preview_comment',
 
                conditions=dict(function=check_repo, method=["POST"]))
 

	
 
    rmap.connect('changeset_comment_delete',
 
                 '/{repo_name:.*?}/changeset/comment/{comment_id}/delete',
 
                controller='changeset', action='delete_comment',
 
                conditions=dict(function=check_repo, method=["DELETE"]))
 

	
 
    rmap.connect('changeset_info', '/changeset_info/{repo_name:.*?}/{revision}',
 
                 controller='changeset', action='changeset_info')
 

	
 
    rmap.connect('compare_url',
 
                 '/{repo_name:.*?}/compare/{org_ref_type}@{org_ref:.*?}...{other_ref_type}@{other_ref:.*?}',
 
                 controller='compare', action='index',
 
                 conditions=dict(function=check_repo),
 
                 requirements=dict(
 
                            org_ref_type='(branch|book|tag|rev|__other_ref_type__)',
 
                            other_ref_type='(branch|book|tag|rev|__org_ref_type__)')
 
                 )
 

	
 
    rmap.connect('pullrequest_home',
 
                 '/{repo_name:.*?}/pull-request/new', controller='pullrequests',
 
                 action='index', conditions=dict(function=check_repo,
 
                                                 method=["GET"]))
 

	
 
    rmap.connect('pullrequest',
 
                 '/{repo_name:.*?}/pull-request/new', controller='pullrequests',
 
                 action='create', conditions=dict(function=check_repo,
 
                                                  method=["POST"]))
 

	
 
    rmap.connect('pullrequest_show',
 
                 '/{repo_name:.*?}/pull-request/{pull_request_id}',
 
                 controller='pullrequests',
 
                 action='show', conditions=dict(function=check_repo,
 
                                                method=["GET"]))
 
    rmap.connect('pullrequest_update',
 
                 '/{repo_name:.*?}/pull-request/{pull_request_id}',
 
                 controller='pullrequests',
 
                 action='update', conditions=dict(function=check_repo,
 
                                                method=["PUT"]))
 
    rmap.connect('pullrequest_delete',
 
                 '/{repo_name:.*?}/pull-request/{pull_request_id}',
 
                 controller='pullrequests',
 
                 action='delete', conditions=dict(function=check_repo,
 
                                                method=["DELETE"]))
 

	
 
    rmap.connect('pullrequest_show_all',
 
                 '/{repo_name:.*?}/pull-request',
 
                 controller='pullrequests',
 
                 action='show_all', conditions=dict(function=check_repo,
 
                                                method=["GET"]))
 

	
 
    rmap.connect('pullrequest_comment',
 
                 '/{repo_name:.*?}/pull-request-comment/{pull_request_id}',
 
                 controller='pullrequests',
 
                 action='comment', conditions=dict(function=check_repo,
 
                                                method=["POST"]))
 

	
 
    rmap.connect('pullrequest_comment_delete',
 
                 '/{repo_name:.*?}/pull-request-comment/{comment_id}/delete',
 
                controller='pullrequests', action='delete_comment',
 
                conditions=dict(function=check_repo, method=["DELETE"]))
 

	
 
    rmap.connect('summary_home_summary', '/{repo_name:.*?}/summary',
 
                controller='summary', conditions=dict(function=check_repo))
 

	
 
    rmap.connect('shortlog_home', '/{repo_name:.*?}/shortlog',
 
                controller='shortlog', conditions=dict(function=check_repo))
 

	
 
    rmap.connect('shortlog_file_home', '/{repo_name:.*?}/shortlog/{revision}/{f_path:.*}',
 
                controller='shortlog', f_path=None,
 
                conditions=dict(function=check_repo))
 

	
 
    rmap.connect('branches_home', '/{repo_name:.*?}/branches',
 
                controller='branches', conditions=dict(function=check_repo))
 

	
 
    rmap.connect('tags_home', '/{repo_name:.*?}/tags',
 
                controller='tags', conditions=dict(function=check_repo))
 

	
 
    rmap.connect('bookmarks_home', '/{repo_name:.*?}/bookmarks',
 
                controller='bookmarks', conditions=dict(function=check_repo))
 

	
 
    rmap.connect('changelog_home', '/{repo_name:.*?}/changelog',
rhodecode/controllers/admin/repos.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
"""
 
    rhodecode.controllers.admin.repos
 
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

	
 
    Repositories controller for RhodeCode
 

	
 
    :created_on: Apr 7, 2010
 
    :author: marcink
 
    :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
 
    :license: GPLv3, see COPYING for more details.
 
"""
 
# This program is free software: you can redistribute it and/or modify
 
# it under the terms of the GNU General Public License as published by
 
# the Free Software Foundation, either version 3 of the License, or
 
# (at your option) any later version.
 
#
 
# This program is distributed in the hope that it will be useful,
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
# GNU General Public License for more details.
 
#
 
# You should have received a copy of the GNU General Public License
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 

	
 
import logging
 
import traceback
 
import formencode
 
from formencode import htmlfill
 

	
 
from webob.exc import HTTPInternalServerError, HTTPForbidden
 
from pylons import request, session, tmpl_context as c, url
 
from pylons.controllers.util import redirect
 
from pylons.i18n.translation import _
 
from sqlalchemy.exc import IntegrityError
 

	
 
import rhodecode
 
from rhodecode.lib import helpers as h
 
from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
 
    HasPermissionAnyDecorator, HasRepoPermissionAllDecorator, NotAnonymous,\
 
    HasPermissionAny, HasReposGroupPermissionAny, HasRepoPermissionAnyDecorator
 
from rhodecode.lib.base import BaseRepoController, render
 
from rhodecode.lib.utils import action_logger, repo_name_slug
 
from rhodecode.lib.helpers import get_token
 
from rhodecode.model.meta import Session
 
from rhodecode.model.db import User, Repository, UserFollowing, RepoGroup,\
 
    RhodeCodeSetting, RepositoryField
 
from rhodecode.model.forms import RepoForm, RepoFieldForm, RepoPermsForm
 
from rhodecode.model.scm import ScmModel, RepoGroupList
 
from rhodecode.model.repo import RepoModel
 
from rhodecode.lib.compat import json
 
from sqlalchemy.sql.expression import func
 
from rhodecode.lib.exceptions import AttachedForksError
 
from rhodecode.lib.utils2 import safe_int
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class ReposController(BaseRepoController):
 
    """
 
    REST Controller styled on the Atom Publishing Protocol"""
 
    # To properly map this controller, ensure your config/routing.py
 
    # file has a resource setup:
 
    #     map.resource('repo', 'repos')
 

	
 
    @LoginRequired()
 
    def __before__(self):
 
        super(ReposController, self).__before__()
 

	
 
    def __load_defaults(self):
 
        acl_groups = RepoGroupList(RepoGroup.query().all(),
 
                               perm_set=['group.write', 'group.admin'])
 
        c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
 
        c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
 

	
 
        repo_model = RepoModel()
 
        c.users_array = repo_model.get_users_js()
 
        c.users_groups_array = repo_model.get_users_groups_js()
 
        choices, c.landing_revs = ScmModel().get_repo_landing_revs()
 
        c.landing_revs_choices = choices
 

	
 
    def __load_data(self, repo_name=None):
 
        """
 
        Load defaults settings for edit, and update
 

	
 
        :param repo_name:
 
        """
 
        self.__load_defaults()
 

	
 
        c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
 
        repo = db_repo.scm_instance
 

	
 
        if c.repo_info is None:
 
            h.not_mapped_error(repo_name)
 
            return redirect(url('repos'))
 

	
 
        ##override defaults for exact repo info here git/hg etc
 
        choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info)
 
        c.landing_revs_choices = choices
 

	
 
        c.default_user_id = User.get_by_username('default').user_id
 
        c.in_public_journal = UserFollowing.query()\
 
            .filter(UserFollowing.user_id == c.default_user_id)\
 
            .filter(UserFollowing.follows_repository == c.repo_info).scalar()
 

	
 
        if c.repo_info.stats:
 
            # this is on what revision we ended up so we add +1 for count
 
            last_rev = c.repo_info.stats.stat_on_revision + 1
 
        else:
 
            last_rev = 0
 
        c.stats_revision = last_rev
 

	
 
        c.repo_last_rev = repo.count() if repo.revisions else 0
 

	
 
        if last_rev == 0 or c.repo_last_rev == 0:
 
            c.stats_percentage = 0
 
        else:
 
            c.stats_percentage = '%.2f' % ((float((last_rev)) /
 
                                            c.repo_last_rev) * 100)
 

	
 
        c.repo_fields = RepositoryField.query()\
 
            .filter(RepositoryField.repository == db_repo).all()
 

	
 
        defaults = RepoModel()._get_defaults(repo_name)
 

	
 
        c.repos_list = [('', _('--REMOVE FORK--'))]
 
        c.repos_list += [(x.repo_id, x.repo_name) for x in
 
                    Repository.query().order_by(Repository.repo_name).all()
 
                    if x.repo_id != c.repo_info.repo_id]
 

	
 
        defaults['id_fork_of'] = db_repo.fork.repo_id if db_repo.fork else ''
 
        return defaults
 

	
 
    @HasPermissionAllDecorator('hg.admin')
 
    def index(self, format='html'):
 
        """GET /repos: All items in the collection"""
 
        # url('repos')
 

	
 
        c.repos_list = Repository.query()\
 
                        .order_by(func.lower(Repository.repo_name))\
 
                        .all()
 

	
 
        repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list,
 
                                                   admin=True,
 
                                                   super_user_actions=True)
 
        #json used to render the grid
 
        c.data = json.dumps(repos_data)
 

	
 
        return render('admin/repos/repos.html')
 

	
 
    @NotAnonymous()
 
    def create(self):
 
        """
 
        POST /repos: Create a new item"""
 
        # url('repos')
 

	
 
        self.__load_defaults()
 
        form_result = {}
 
        try:
 
            form_result = RepoForm(repo_groups=c.repo_groups_choices,
 
                                   landing_revs=c.landing_revs_choices)()\
 
                            .to_python(dict(request.POST))
 

	
 
            new_repo = RepoModel().create(form_result,
 
                                          self.rhodecode_user.user_id)
 
            if form_result['clone_uri']:
 
                h.flash(_('Created repository %s from %s') \
 
                    % (form_result['repo_name'], form_result['clone_uri']),
 
                    category='success')
 
            else:
 
                repo_url = h.link_to(form_result['repo_name'],
 
                    h.url('summary_home', repo_name=form_result['repo_name_full']))
 
                h.flash(h.literal(_('Created repository %s') % repo_url),
 
                        category='success')
 

	
 
            if request.POST.get('user_created'):
 
                # created by regular non admin user
 
                action_logger(self.rhodecode_user, 'user_created_repo',
 
                              form_result['repo_name_full'], self.ip_addr,
 
                              self.sa)
 
            else:
 
                action_logger(self.rhodecode_user, 'admin_created_repo',
 
                              form_result['repo_name_full'], self.ip_addr,
 
                              self.sa)
 
            Session().commit()
 
        except formencode.Invalid, errors:
 
            return htmlfill.render(
 
                render('admin/repos/repo_add.html'),
 
                defaults=errors.value,
 
                errors=errors.error_dict or {},
 
                prefix_error=False,
 
                encoding="UTF-8")
 

	
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            msg = _('Error creating repository %s') \
 
                    % form_result.get('repo_name')
 
            h.flash(msg, category='error')
 
            if c.rhodecode_user.is_admin:
 
                return redirect(url('repos'))
 
            return redirect(url('home'))
 
        #redirect to our new repo !
 
        return redirect(url('summary_home', repo_name=new_repo.repo_name))
 

	
 
    @NotAnonymous()
 
    def create_repository(self):
 
        """GET /_admin/create_repository: Form to create a new item"""
 
        new_repo = request.GET.get('repo', '')
 
        parent_group = request.GET.get('parent_group')
 
        if not HasPermissionAny('hg.admin', 'hg.create.repository')():
 
            #you're not super admin nor have global create permissions,
 
            #but maybe you have at least write permission to a parent group ?
 
            _gr = RepoGroup.get(parent_group)
 
            gr_name = _gr.group_name if _gr else None
 
            if not HasReposGroupPermissionAny('group.admin', 'group.write')(group_name=gr_name):
 
                raise HTTPForbidden
 

	
 
        acl_groups = RepoGroupList(RepoGroup.query().all(),
 
                               perm_set=['group.write', 'group.admin'])
 
        c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
 
        c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
 
        choices, c.landing_revs = ScmModel().get_repo_landing_revs()
 

	
 
        c.new_repo = repo_name_slug(new_repo)
 

	
 
        ## apply the defaults from defaults page
 
        defaults = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
 
        if parent_group:
 
            defaults.update({'repo_group': parent_group})
 

	
 
        return htmlfill.render(
 
            render('admin/repos/repo_add.html'),
 
            defaults=defaults,
 
            errors={},
 
            prefix_error=False,
 
            encoding="UTF-8"
 
        )
 

	
 
    @HasRepoPermissionAllDecorator('repository.admin')
 
    def update(self, repo_name):
 
        """
 
        PUT /repos/repo_name: Update an existing item"""
 
        # Forms posted to this method should contain a hidden field:
 
        #    <input type="hidden" name="_method" value="PUT" />
 
        # Or using helpers:
 
        #    h.form(url('repo', repo_name=ID),
 
        #           method='put')
 
        # url('repo', repo_name=ID)
 
        self.__load_defaults()
 
        repo_model = RepoModel()
 
        changed_name = repo_name
 
        #override the choices with extracted revisions !
 
        choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo_name)
 
        c.landing_revs_choices = choices
 
        repo = Repository.get_by_repo_name(repo_name)
 
        _form = RepoForm(edit=True, old_data={'repo_name': repo_name,
 
                                              'repo_group': repo.group.get_dict() \
 
                                              if repo.group else {}},
 
                         repo_groups=c.repo_groups_choices,
 
                         landing_revs=c.landing_revs_choices)()
 
        try:
 
            form_result = _form.to_python(dict(request.POST))
 
            repo = repo_model.update(repo_name, **form_result)
 
            ScmModel().mark_for_invalidation(repo_name)
 
            h.flash(_('Repository %s updated successfully') % repo_name,
 
                    category='success')
 
            changed_name = repo.repo_name
 
            action_logger(self.rhodecode_user, 'admin_updated_repo',
 
                              changed_name, self.ip_addr, self.sa)
 
            Session().commit()
 
        except formencode.Invalid, errors:
 
            defaults = self.__load_data(repo_name)
 
            defaults.update(errors.value)
 
            return htmlfill.render(
 
                render('admin/repos/repo_edit.html'),
 
                defaults=defaults,
 
                errors=errors.error_dict or {},
 
                prefix_error=False,
 
                encoding="UTF-8")
 

	
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('Error occurred during update of repository %s') \
 
                    % repo_name, category='error')
 
        return redirect(url('edit_repo', repo_name=changed_name))
 

	
 
    @HasRepoPermissionAllDecorator('repository.admin')
 
    def delete(self, repo_name):
 
        """
 
        DELETE /repos/repo_name: Delete an existing item"""
 
        # Forms posted to this method should contain a hidden field:
 
        #    <input type="hidden" name="_method" value="DELETE" />
 
        # Or using helpers:
 
        #    h.form(url('repo', repo_name=ID),
 
        #           method='delete')
 
        # url('repo', repo_name=ID)
 

	
 
        repo_model = RepoModel()
 
        repo = repo_model.get_by_repo_name(repo_name)
 
        if not repo:
 
            h.not_mapped_error(repo_name)
 
            return redirect(url('repos'))
 
        try:
 
            _forks = repo.forks.count()
 
            handle_forks = None
 
            if _forks and request.POST.get('forks'):
 
                do = request.POST['forks']
 
                if do == 'detach_forks':
 
                    handle_forks = 'detach'
 
                    h.flash(_('Detached %s forks') % _forks, category='success')
 
                elif do == 'delete_forks':
 
                    handle_forks = 'delete'
 
                    h.flash(_('Deleted %s forks') % _forks, category='success')
 
            repo_model.delete(repo, forks=handle_forks)
 
            action_logger(self.rhodecode_user, 'admin_deleted_repo',
 
                  repo_name, self.ip_addr, self.sa)
 
            ScmModel().mark_for_invalidation(repo_name)
 
            h.flash(_('Deleted repository %s') % repo_name, category='success')
 
            Session().commit()
 
        except AttachedForksError:
 
            h.flash(_('Cannot delete %s it still contains attached forks')
 
                        % repo_name, category='warning')
 

	
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('An error occurred during deletion of %s') % repo_name,
 
                    category='error')
 

	
 
        return redirect(url('repos'))
 

	
 
    @HasRepoPermissionAllDecorator('repository.admin')
 
    def set_repo_perm_member(self, repo_name):
 
        form = RepoPermsForm()().to_python(request.POST)
 
        RepoModel()._update_permissions(repo_name, form['perms_new'],
 
                                        form['perms_updates'])
 
        #TODO: implement this
 
        #action_logger(self.rhodecode_user, 'admin_changed_repo_permissions',
 
        #              repo_name, self.ip_addr, self.sa)
 
        Session().commit()
 
        h.flash(_('Repository permissions updated'), category='success')
 
        return redirect(url('edit_repo', repo_name=repo_name))
 

	
 
    @HasRepoPermissionAllDecorator('repository.admin')
 
    def delete_perm_user(self, repo_name):
 
    def delete_repo_perm_member(self, repo_name):
 
        """
 
        DELETE an existing repository permission user
 

	
 
        :param repo_name:
 
        """
 
        try:
 
            RepoModel().revoke_user_permission(repo=repo_name,
 
                                               user=request.POST['user_id'])
 
            obj_type = request.POST.get('obj_type')
 
            obj_id = None
 
            if obj_type == 'user':
 
                obj_id = safe_int(request.POST.get('user_id'))
 
            elif obj_type == 'user_group':
 
                obj_id = safe_int(request.POST.get('user_group_id'))
 

	
 
            if obj_type == 'user':
 
                RepoModel().revoke_user_permission(repo=repo_name, user=obj_id)
 
            elif obj_type == 'user_group':
 
                RepoModel().revoke_users_group_permission(
 
                    repo=repo_name, group_name=obj_id
 
                )
 
            #TODO: implement this
 
            #action_logger(self.rhodecode_user, 'admin_revoked_repo_permissions',
 
            #              repo_name, self.ip_addr, self.sa)
 
            Session().commit()
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('An error occurred during deletion of repository user'),
 
                    category='error')
 
            raise HTTPInternalServerError()
 

	
 
    @HasRepoPermissionAllDecorator('repository.admin')
 
    def delete_perm_users_group(self, repo_name):
 
        """
 
        DELETE an existing repository permission user group
 

	
 
        :param repo_name:
 
        """
 

	
 
        try:
 
            RepoModel().revoke_users_group_permission(
 
                repo=repo_name, group_name=request.POST['users_group_id']
 
            )
 
            Session().commit()
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('An error occurred during deletion of repository'
 
                      ' user groups'),
 
            h.flash(_('An error occurred during revoking of permission'),
 
                    category='error')
 
            raise HTTPInternalServerError()
 

	
 
    @HasRepoPermissionAllDecorator('repository.admin')
 
    def repo_stats(self, repo_name):
 
        """
 
        DELETE an existing repository statistics
 

	
 
        :param repo_name:
 
        """
 

	
 
        try:
 
            RepoModel().delete_stats(repo_name)
 
            Session().commit()
 
        except Exception, e:
 
            log.error(traceback.format_exc())
 
            h.flash(_('An error occurred during deletion of repository stats'),
 
                    category='error')
 
        return redirect(url('edit_repo', repo_name=repo_name))
 

	
 
    @HasRepoPermissionAllDecorator('repository.admin')
 
    def repo_cache(self, repo_name):
 
        """
 
        INVALIDATE existing repository cache
 

	
 
        :param repo_name:
 
        """
 

	
 
        try:
 
            ScmModel().mark_for_invalidation(repo_name)
 
            Session().commit()
 
        except Exception, e:
 
            log.error(traceback.format_exc())
 
            h.flash(_('An error occurred during cache invalidation'),
 
                    category='error')
 
        return redirect(url('edit_repo', repo_name=repo_name))
 

	
 
    @HasRepoPermissionAllDecorator('repository.admin')
 
    def repo_locking(self, repo_name):
 
        """
 
        Unlock repository when it is locked !
 

	
 
        :param repo_name:
 
        """
 

	
 
        try:
 
            repo = Repository.get_by_repo_name(repo_name)
 
            if request.POST.get('set_lock'):
 
                Repository.lock(repo, c.rhodecode_user.user_id)
 
            elif request.POST.get('set_unlock'):
 
                Repository.unlock(repo)
 
        except Exception, e:
 
            log.error(traceback.format_exc())
 
            h.flash(_('An error occurred during unlocking'),
 
                    category='error')
 
        return redirect(url('edit_repo', repo_name=repo_name))
 

	
 
    @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
 
    def toggle_locking(self, repo_name):
 
        """
 
        Toggle locking of repository by simple GET call to url
 

	
 
        :param repo_name:
 
        """
 

	
 
        try:
 
            repo = Repository.get_by_repo_name(repo_name)
 

	
 
            if repo.enable_locking:
 
                if repo.locked[0]:
 
                    Repository.unlock(repo)
 
                    action = _('Unlocked')
 
                else:
 
                    Repository.lock(repo, c.rhodecode_user.user_id)
 
                    action = _('Locked')
 

	
 
                h.flash(_('Repository has been %s') % action,
 
                        category='success')
 
        except Exception, e:
 
            log.error(traceback.format_exc())
 
            h.flash(_('An error occurred during unlocking'),
 
                    category='error')
 
        return redirect(url('summary_home', repo_name=repo_name))
 

	
 
    @HasRepoPermissionAllDecorator('repository.admin')
 
    def repo_public_journal(self, repo_name):
 
        """
 
        Set's this repository to be visible in public journal,
 
        in other words assing default user to follow this repo
 

	
 
        :param repo_name:
 
        """
 

	
 
        cur_token = request.POST.get('auth_token')
 
        token = get_token()
 
        if cur_token == token:
 
            try:
 
                repo_id = Repository.get_by_repo_name(repo_name).repo_id
 
                user_id = User.get_by_username('default').user_id
 
                self.scm_model.toggle_following_repo(repo_id, user_id)
 
                h.flash(_('Updated repository visibility in public journal'),
 
                        category='success')
 
                Session().commit()
 
            except Exception:
 
                h.flash(_('An error occurred during setting this'
 
                          ' repository in public journal'),
 
                        category='error')
 

	
 
        else:
 
            h.flash(_('Token mismatch'), category='error')
 
        return redirect(url('edit_repo', repo_name=repo_name))
 

	
 
    @HasRepoPermissionAllDecorator('repository.admin')
 
    def repo_pull(self, repo_name):
 
        """
 
        Runs task to update given repository with remote changes,
 
        ie. make pull on remote location
 

	
 
        :param repo_name:
 
        """
 
        try:
 
            ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
 
            h.flash(_('Pulled from remote location'), category='success')
 
        except Exception, e:
 
            h.flash(_('An error occurred during pull from remote location'),
 
                    category='error')
 

	
 
        return redirect(url('edit_repo', repo_name=repo_name))
 

	
 
    @HasRepoPermissionAllDecorator('repository.admin')
 
    def repo_as_fork(self, repo_name):
 
        """
 
        Mark given repository as a fork of another
 

	
 
        :param repo_name:
 
        """
 
        try:
 
            fork_id = request.POST.get('id_fork_of')
 
            repo = ScmModel().mark_as_fork(repo_name, fork_id,
 
                                    self.rhodecode_user.username)
 
            fork = repo.fork.repo_name if repo.fork else _('Nothing')
 
            Session().commit()
 
            h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork),
 
                    category='success')
 
        except Exception, e:
 
            log.error(traceback.format_exc())
 
            h.flash(_('An error occurred during this operation'),
 
                    category='error')
 

	
 
        return redirect(url('edit_repo', repo_name=repo_name))
 

	
 
    @HasPermissionAllDecorator('hg.admin')
 
    def show(self, repo_name, format='html'):
 
        """GET /repos/repo_name: Show a specific item"""
 
        # url('repo', repo_name=ID)
 

	
 
    @HasRepoPermissionAllDecorator('repository.admin')
 
    def edit(self, repo_name, format='html'):
 
        """GET /repos/repo_name/edit: Form to edit an existing item"""
 
        # url('edit_repo', repo_name=ID)
 
        defaults = self.__load_data(repo_name)
 

	
 
        return htmlfill.render(
 
            render('admin/repos/repo_edit.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False
 
        )
 

	
 
    @HasPermissionAllDecorator('hg.admin')
 
    def create_repo_field(self, repo_name):
 
        try:
 
            form_result = RepoFieldForm()().to_python(dict(request.POST))
 
            new_field = RepositoryField()
 
            new_field.repository = Repository.get_by_repo_name(repo_name)
 
            new_field.field_key = form_result['new_field_key']
 
            new_field.field_type = form_result['new_field_type']  # python type
 
            new_field.field_value = form_result['new_field_value']  # set initial blank value
 
            new_field.field_desc = form_result['new_field_desc']
 
            new_field.field_label = form_result['new_field_label']
 
            Session().add(new_field)
 
            Session().commit()
 

	
 
        except Exception, e:
 
            log.error(traceback.format_exc())
 
            msg = _('An error occurred during creation of field')
 
            if isinstance(e, formencode.Invalid):
 
                msg += ". " + e.msg
 
            h.flash(msg, category='error')
 
        return redirect(url('edit_repo', repo_name=repo_name))
 

	
 
    @HasPermissionAllDecorator('hg.admin')
 
    def delete_repo_field(self, repo_name, field_id):
 
        field = RepositoryField.get_or_404(field_id)
 
        try:
 
            Session().delete(field)
 
            Session().commit()
 
        except Exception, e:
 
            log.error(traceback.format_exc())
 
            msg = _('An error occurred during removal of field')
 
            h.flash(msg, category='error')
 
        return redirect(url('edit_repo', repo_name=repo_name))
rhodecode/controllers/admin/repos_groups.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
"""
 
    rhodecode.controllers.admin.repos_groups
 
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

	
 
    Repository groups controller for RhodeCode
 

	
 
    :created_on: Mar 23, 2010
 
    :author: marcink
 
    :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
 
    :license: GPLv3, see COPYING for more details.
 
"""
 
# This program is free software: you can redistribute it and/or modify
 
# it under the terms of the GNU General Public License as published by
 
# the Free Software Foundation, either version 3 of the License, or
 
# (at your option) any later version.
 
#
 
# This program is distributed in the hope that it will be useful,
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
# GNU General Public License for more details.
 
#
 
# You should have received a copy of the GNU General Public License
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 

	
 
import logging
 
import traceback
 
import formencode
 

	
 
from formencode import htmlfill
 

	
 
from pylons import request, tmpl_context as c, url
 
from pylons.controllers.util import abort, redirect
 
from pylons.i18n.translation import _
 

	
 
from sqlalchemy.exc import IntegrityError
 

	
 
import rhodecode
 
from rhodecode.lib import helpers as h
 
from rhodecode.lib.compat import json
 
from rhodecode.lib.auth import LoginRequired, HasPermissionAnyDecorator,\
 
    HasReposGroupPermissionAnyDecorator, HasReposGroupPermissionAll,\
 
    HasPermissionAll
 
from rhodecode.lib.base import BaseController, render
 
from rhodecode.model.db import RepoGroup, Repository
 
from rhodecode.model.scm import RepoGroupList
 
from rhodecode.model.repos_group import ReposGroupModel
 
from rhodecode.model.forms import ReposGroupForm, RepoGroupPermsForm
 
from rhodecode.model.meta import Session
 
from rhodecode.model.repo import RepoModel
 
from webob.exc import HTTPInternalServerError, HTTPNotFound
 
from rhodecode.lib.utils2 import str2bool, safe_int
 
from sqlalchemy.sql.expression import func
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class ReposGroupsController(BaseController):
 
    """REST Controller styled on the Atom Publishing Protocol"""
 
    # To properly map this controller, ensure your config/routing.py
 
    # file has a resource setup:
 
    #     map.resource('repos_group', 'repos_groups')
 

	
 
    @LoginRequired()
 
    def __before__(self):
 
        super(ReposGroupsController, self).__before__()
 

	
 
    def __load_defaults(self, allow_empty_group=False, exclude_group_ids=[]):
 
        if HasPermissionAll('hg.admin')('group edit'):
 
            #we're global admin, we're ok and we can create TOP level groups
 
            allow_empty_group = True
 

	
 
        #override the choices for this form, we need to filter choices
 
        #and display only those we have ADMIN right
 
        groups_with_admin_rights = RepoGroupList(RepoGroup.query().all(),
 
                                             perm_set=['group.admin'])
 
        c.repo_groups = RepoGroup.groups_choices(groups=groups_with_admin_rights,
 
                                                 show_empty_group=allow_empty_group)
 
        # exclude filtered ids
 
        c.repo_groups = filter(lambda x: x[0] not in exclude_group_ids,
 
                               c.repo_groups)
 
        c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
 
        repo_model = RepoModel()
 
        c.users_array = repo_model.get_users_js()
 
        c.users_groups_array = repo_model.get_users_groups_js()
 

	
 
    def __load_data(self, group_id):
 
        """
 
        Load defaults settings for edit, and update
 

	
 
        :param group_id:
 
        """
 
        repo_group = RepoGroup.get_or_404(group_id)
 
        data = repo_group.get_dict()
 
        data['group_name'] = repo_group.name
 

	
 
        # fill repository group users
 
        for p in repo_group.repo_group_to_perm:
 
            data.update({'u_perm_%s' % p.user.username:
 
                             p.permission.permission_name})
 

	
 
        # fill repository group groups
 
        for p in repo_group.users_group_to_perm:
 
            data.update({'g_perm_%s' % p.users_group.users_group_name:
 
                             p.permission.permission_name})
 

	
 
        return data
 

	
 
    def _revoke_perms_on_yourself(self, form_result):
 
        _up = filter(lambda u: c.rhodecode_user.username == u[0],
 
                     form_result['perms_updates'])
 
        _new = filter(lambda u: c.rhodecode_user.username == u[0],
 
                      form_result['perms_new'])
 
        if _new and _new[0][1] != 'group.admin' or _up and _up[0][1] != 'group.admin':
 
            return True
 
        return False
 

	
 
    def index(self, format='html'):
 
        """GET /repos_groups: All items in the collection"""
 
        # url('repos_groups')
 
        group_iter = RepoGroupList(RepoGroup.query().all(),
 
                                   perm_set=['group.admin'])
 
        sk = lambda g: g.parents[0].group_name if g.parents else g.group_name
 
        c.groups = sorted(group_iter, key=sk)
 
        return render('admin/repos_groups/repos_groups_show.html')
 

	
 
    def create(self):
 
        """POST /repos_groups: Create a new item"""
 
        # url('repos_groups')
 

	
 
        self.__load_defaults()
 

	
 
        # permissions for can create group based on parent_id are checked
 
        # here in the Form
 
        repos_group_form = ReposGroupForm(available_groups=
 
                                map(lambda k: unicode(k[0]), c.repo_groups))()
 
        try:
 
            form_result = repos_group_form.to_python(dict(request.POST))
 
            ReposGroupModel().create(
 
                    group_name=form_result['group_name'],
 
                    group_description=form_result['group_description'],
 
                    parent=form_result['group_parent_id'],
 
                    owner=self.rhodecode_user.user_id
 
            )
 
            Session().commit()
 
            h.flash(_('Created repository group %s') \
 
                    % form_result['group_name'], category='success')
 
            #TODO: in futureaction_logger(, '', '', '', self.sa)
 
        except formencode.Invalid, errors:
 
            return htmlfill.render(
 
                render('admin/repos_groups/repos_groups_add.html'),
 
                defaults=errors.value,
 
                errors=errors.error_dict or {},
 
                prefix_error=False,
 
                encoding="UTF-8")
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('Error occurred during creation of repository group %s') \
 
                    % request.POST.get('group_name'), category='error')
 
        parent_group_id = form_result['group_parent_id']
 
        #TODO: maybe we should get back to the main view, not the admin one
 
        return redirect(url('repos_groups', parent_group=parent_group_id))
 

	
 
    def new(self, format='html'):
 
        """GET /repos_groups/new: Form to create a new item"""
 
        # url('new_repos_group')
 
        if HasPermissionAll('hg.admin')('group create'):
 
            #we're global admin, we're ok and we can create TOP level groups
 
            pass
 
        else:
 
            # we pass in parent group into creation form, thus we know
 
            # what would be the group, we can check perms here !
 
            group_id = safe_int(request.GET.get('parent_group'))
 
            group = RepoGroup.get(group_id) if group_id else None
 
            group_name = group.group_name if group else None
 
            if HasReposGroupPermissionAll('group.admin')(group_name, 'group create'):
 
                pass
 
            else:
 
                return abort(403)
 

	
 
        self.__load_defaults()
 
        return render('admin/repos_groups/repos_groups_add.html')
 

	
 
    @HasReposGroupPermissionAnyDecorator('group.admin')
 
    def update(self, group_name):
 
        """PUT /repos_groups/group_name: Update an existing item"""
 
        # Forms posted to this method should contain a hidden field:
 
        #    <input type="hidden" name="_method" value="PUT" />
 
        # Or using helpers:
 
        #    h.form(url('repos_group', group_name=GROUP_NAME),
 
        #           method='put')
 
        # url('repos_group', group_name=GROUP_NAME)
 

	
 
        c.repos_group = ReposGroupModel()._get_repo_group(group_name)
 
        if HasPermissionAll('hg.admin')('group edit'):
 
            #we're global admin, we're ok and we can create TOP level groups
 
            allow_empty_group = True
 
        elif not c.repos_group.parent_group:
 
            allow_empty_group = True
 
        else:
 
            allow_empty_group = False
 
        self.__load_defaults(allow_empty_group=allow_empty_group,
 
                             exclude_group_ids=[c.repos_group.group_id])
 

	
 
        repos_group_form = ReposGroupForm(
 
            edit=True,
 
            old_data=c.repos_group.get_dict(),
 
            available_groups=c.repo_groups_choices,
 
            can_create_in_root=allow_empty_group,
 
        )()
 
        try:
 
            form_result = repos_group_form.to_python(dict(request.POST))
 
            if not c.rhodecode_user.is_admin:
 
                if self._revoke_perms_on_yourself(form_result):
 
                    msg = _('Cannot revoke permission for yourself as admin')
 
                    h.flash(msg, category='warning')
 
                    raise Exception('revoke admin permission on self')
 

	
 
            new_gr = ReposGroupModel().update(group_name, form_result)
 
            Session().commit()
 
            h.flash(_('Updated repository group %s') \
 
                    % form_result['group_name'], category='success')
 
            # we now have new name !
 
            group_name = new_gr.group_name
 
            #TODO: in future action_logger(, '', '', '', self.sa)
 
        except formencode.Invalid, errors:
 

	
 
            return htmlfill.render(
 
                render('admin/repos_groups/repos_groups_edit.html'),
 
                defaults=errors.value,
 
                errors=errors.error_dict or {},
 
                prefix_error=False,
 
                encoding="UTF-8")
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('Error occurred during update of repository group %s') \
 
                    % request.POST.get('group_name'), category='error')
 

	
 
        return redirect(url('edit_repos_group', group_name=group_name))
 

	
 
    @HasReposGroupPermissionAnyDecorator('group.admin')
 
    def delete(self, group_name):
 
        """DELETE /repos_groups/group_name: Delete an existing item"""
 
        # Forms posted to this method should contain a hidden field:
 
        #    <input type="hidden" name="_method" value="DELETE" />
 
        # Or using helpers:
 
        #    h.form(url('repos_group', group_name=GROUP_NAME),
 
        #           method='delete')
 
        # url('repos_group', group_name=GROUP_NAME)
 

	
 
        gr = c.repos_group = ReposGroupModel()._get_repo_group(group_name)
 
        repos = gr.repositories.all()
 
        if repos:
 
            h.flash(_('This group contains %s repositores and cannot be '
 
                      'deleted') % len(repos), category='warning')
 
            return redirect(url('repos_groups'))
 

	
 
        children = gr.children.all()
 
        if children:
 
            h.flash(_('This group contains %s subgroups and cannot be deleted'
 
                      % (len(children))), category='warning')
 
            return redirect(url('repos_groups'))
 

	
 
        try:
 
            ReposGroupModel().delete(group_name)
 
            Session().commit()
 
            h.flash(_('Removed repository group %s') % group_name,
 
                    category='success')
 
            #TODO: in future action_logger(, '', '', '', self.sa)
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('Error occurred during deletion of repos '
 
                      'group %s') % group_name, category='error')
 

	
 
        return redirect(url('repos_groups'))
 

	
 
    @HasReposGroupPermissionAnyDecorator('group.admin')
 
    def set_repo_group_perm_member(self, group_name):
 
        c.repos_group = ReposGroupModel()._get_repo_group(group_name)
 
        form = RepoGroupPermsForm()().to_python(request.POST)
 

	
 
        recursive = form['recursive']
 
        # iterate over all members(if in recursive mode) of this groups and
 
        # set the permissions !
 
        # this can be potentially heavy operation
 
        ReposGroupModel()._update_permissions(c.repos_group, form['perms_new'],
 
                                              form['perms_updates'], recursive)
 
        #TODO: implement this
 
        #action_logger(self.rhodecode_user, 'admin_changed_repo_permissions',
 
        #              repo_name, self.ip_addr, self.sa)
 
        Session().commit()
 
        h.flash(_('Repository Group permissions updated'), category='success')
 
        return redirect(url('edit_repos_group', group_name=group_name))
 

	
 
    @HasReposGroupPermissionAnyDecorator('group.admin')
 
    def delete_repos_group_user_perm(self, group_name):
 
    def delete_repo_group_perm_member(self, group_name):
 
        """
 
        DELETE an existing repository group permission user
 

	
 
        :param group_name:
 
        """
 
        try:
 
            obj_type = request.POST.get('obj_type')
 
            obj_id = None
 
            if obj_type == 'user':
 
                obj_id = safe_int(request.POST.get('user_id'))
 
            elif obj_type == 'user_group':
 
                obj_id = safe_int(request.POST.get('user_group_id'))
 

	
 
            if not c.rhodecode_user.is_admin:
 
                if c.rhodecode_user.user_id == safe_int(request.POST['user_id']):
 
                if obj_type == 'user' and c.rhodecode_user.user_id == obj_id:
 
                    msg = _('Cannot revoke permission for yourself as admin')
 
                    h.flash(msg, category='warning')
 
                    raise Exception('revoke admin permission on self')
 
            recursive = str2bool(request.POST.get('recursive', False))
 
            if obj_type == 'user':
 
            ReposGroupModel().delete_permission(
 
                repos_group=group_name, obj=request.POST['user_id'],
 
                    repos_group=group_name, obj=obj_id,
 
                obj_type='user', recursive=recursive
 
            )
 
            elif obj_type == 'user_group':
 
                ReposGroupModel().delete_permission(
 
                    repos_group=group_name, obj=obj_id,
 
                    obj_type='users_group', recursive=recursive
 
                )
 

	
 
            Session().commit()
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('An error occurred during deletion of group user'),
 
                    category='error')
 
            raise HTTPInternalServerError()
 

	
 
    @HasReposGroupPermissionAnyDecorator('group.admin')
 
    def delete_repos_group_users_group_perm(self, group_name):
 
        """
 
        DELETE an existing repository group permission user group
 

	
 
        :param group_name:
 
        """
 

	
 
        try:
 
            recursive = str2bool(request.POST.get('recursive', False))
 
            ReposGroupModel().delete_permission(
 
                repos_group=group_name, obj=request.POST['users_group_id'],
 
                obj_type='users_group', recursive=recursive
 
            )
 
            Session().commit()
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('An error occurred during deletion of group'
 
                      ' user groups'),
 
            h.flash(_('An error occurred during revoking of permission'),
 
                    category='error')
 
            raise HTTPInternalServerError()
 

	
 
    def show_by_name(self, group_name):
 
        """
 
        This is a proxy that does a lookup group_name -> id, and shows
 
        the group by id view instead
 
        """
 
        group_name = group_name.rstrip('/')
 
        id_ = RepoGroup.get_by_group_name(group_name)
 
        if id_:
 
            return self.show(id_.group_id)
 
        raise HTTPNotFound
 

	
 
    @HasReposGroupPermissionAnyDecorator('group.read', 'group.write',
 
                                         'group.admin')
 
    def show(self, group_name, format='html'):
 
        """GET /repos_groups/group_name: Show a specific item"""
 
        # url('repos_group', group_name=GROUP_NAME)
 

	
 
        c.group = c.repos_group = ReposGroupModel()._get_repo_group(group_name)
 
        c.group_repos = c.group.repositories.all()
 

	
 
        #overwrite our cached list with current filter
 
        gr_filter = c.group_repos
 
        c.repo_cnt = 0
 

	
 
        groups = RepoGroup.query().order_by(RepoGroup.group_name)\
 
            .filter(RepoGroup.group_parent_id == c.group.group_id).all()
 
        c.groups = self.scm_model.get_repos_groups(groups)
 

	
 
        if not c.visual.lightweight_dashboard:
 
            c.repos_list = self.scm_model.get_repos(all_repos=gr_filter)
 
        ## lightweight version of dashboard
 
        else:
 
            c.repos_list = Repository.query()\
 
                            .filter(Repository.group_id == c.group.group_id)\
 
                            .order_by(func.lower(Repository.repo_name))\
 
                            .all()
 

	
 
            repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list,
 
                                                       admin=False)
 
            #json used to render the grid
 
            c.data = json.dumps(repos_data)
 

	
 
        return render('admin/repos_groups/repos_groups.html')
 

	
 
    @HasReposGroupPermissionAnyDecorator('group.admin')
 
    def edit(self, group_name, format='html'):
 
        """GET /repos_groups/group_name/edit: Form to edit an existing item"""
 
        # url('edit_repos_group', group_name=GROUP_NAME)
 

	
 
        c.repos_group = ReposGroupModel()._get_repo_group(group_name)
 
        #we can only allow moving empty group if it's already a top-level
 
        #group, ie has no parents, or we're admin
 
        if HasPermissionAll('hg.admin')('group edit'):
 
            #we're global admin, we're ok and we can create TOP level groups
 
            allow_empty_group = True
 
        elif not c.repos_group.parent_group:
 
            allow_empty_group = True
 
        else:
 
            allow_empty_group = False
 

	
 
        self.__load_defaults(allow_empty_group=allow_empty_group,
 
                             exclude_group_ids=[c.repos_group.group_id])
 
        defaults = self.__load_data(c.repos_group.group_id)
 

	
 
        return htmlfill.render(
 
            render('admin/repos_groups/repos_groups_edit.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False
 
        )
rhodecode/controllers/admin/users_groups.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
"""
 
    rhodecode.controllers.admin.users_groups
 
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

	
 
    User Groups crud controller for pylons
 

	
 
    :created_on: Jan 25, 2011
 
    :author: marcink
 
    :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
 
    :license: GPLv3, see COPYING for more details.
 
"""
 
# This program is free software: you can redistribute it and/or modify
 
# it under the terms of the GNU General Public License as published by
 
# the Free Software Foundation, either version 3 of the License, or
 
# (at your option) any later version.
 
#
 
# This program is distributed in the hope that it will be useful,
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
# GNU General Public License for more details.
 
#
 
# You should have received a copy of the GNU General Public License
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 

	
 
import logging
 
import traceback
 
import formencode
 

	
 
from formencode import htmlfill
 
from pylons import request, session, tmpl_context as c, url, config
 
from pylons.controllers.util import abort, redirect
 
from pylons.i18n.translation import _
 

	
 
from rhodecode.lib import helpers as h
 
from rhodecode.lib.exceptions import UserGroupsAssignedException
 
from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
 
from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator,\
 
    HasUserGroupPermissionAnyDecorator
 
from rhodecode.lib.base import BaseController, render
 
from rhodecode.model.scm import UserGroupList
 
from rhodecode.model.users_group import UserGroupModel
 
from rhodecode.model.repo import RepoModel
 
from rhodecode.model.db import User, UserGroup, UserGroupToPerm,\
 
    UserGroupRepoToPerm, UserGroupRepoGroupToPerm
 
from rhodecode.model.forms import UserGroupForm, UserGroupPermsForm
 
from rhodecode.model.meta import Session
 
from rhodecode.lib.utils import action_logger
 
from sqlalchemy.orm import joinedload
 
from webob.exc import HTTPInternalServerError
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class UsersGroupsController(BaseController):
 
    """REST Controller styled on the Atom Publishing Protocol"""
 
    # To properly map this controller, ensure your config/routing.py
 
    # file has a resource setup:
 
    #     map.resource('users_group', 'users_groups')
 

	
 
    @LoginRequired()
 
    def __before__(self):
 
        super(UsersGroupsController, self).__before__()
 
        c.available_permissions = config['available_permissions']
 

	
 
    def __load_data(self, user_group_id):
 
        ugroup_repo_perms = UserGroupRepoToPerm.query()\
 
            .options(joinedload(UserGroupRepoToPerm.permission))\
 
            .options(joinedload(UserGroupRepoToPerm.repository))\
 
            .filter(UserGroupRepoToPerm.users_group_id == user_group_id)\
 
            .all()
 

	
 
        for gr in ugroup_repo_perms:
 
            c.users_group.permissions['repositories'][gr.repository.repo_name]  \
 
                = gr.permission.permission_name
 

	
 
        ugroup_group_perms = UserGroupRepoGroupToPerm.query()\
 
            .options(joinedload(UserGroupRepoGroupToPerm.permission))\
 
            .options(joinedload(UserGroupRepoGroupToPerm.group))\
 
            .filter(UserGroupRepoGroupToPerm.users_group_id == user_group_id)\
 
            .all()
 

	
 
        for gr in ugroup_group_perms:
 
            c.users_group.permissions['repositories_groups'][gr.group.group_name] \
 
                = gr.permission.permission_name
 

	
 
        c.group_members_obj = sorted((x.user for x in c.users_group.members),
 
                                     key=lambda u: u.username.lower())
 

	
 
        c.group_members = [(x.user_id, x.username) for x in c.group_members_obj]
 
        c.available_members = sorted(((x.user_id, x.username) for x in
 
                                      User.query().all()),
 
                                     key=lambda u: u[1].lower())
 
        repo_model = RepoModel()
 
        c.users_array = repo_model.get_users_js()
 

	
 
        # commented out due to not now supporting assignment for user group
 
        # on user group
 
        c.users_groups_array = "[]"  # repo_model.get_users_groups_js()
 
        c.available_permissions = config['available_permissions']
 

	
 
    def __load_defaults(self, user_group_id):
 
        """
 
        Load defaults settings for edit, and update
 

	
 
        :param user_group_id:
 
        """
 
        user_group = UserGroup.get_or_404(user_group_id)
 
        data = user_group.get_dict()
 

	
 
        ug_model = UserGroupModel()
 

	
 
        data.update({
 
            'create_repo_perm': ug_model.has_perm(user_group,
 
                                                  'hg.create.repository'),
 
            'fork_repo_perm': ug_model.has_perm(user_group,
 
                                                'hg.fork.repository'),
 
        })
 

	
 
        # fill user group users
 
        for p in user_group.user_user_group_to_perm:
 
            data.update({'u_perm_%s' % p.user.username:
 
                             p.permission.permission_name})
 

	
 
        return data
 

	
 
    def index(self, format='html'):
 
        """GET /users_groups: All items in the collection"""
 
        # url('users_groups')
 

	
 
        group_iter = UserGroupList(UserGroup().query().all(),
 
                                   perm_set=['usergroup.admin'])
 
        sk = lambda g: g.users_group_name
 
        c.users_groups_list = sorted(group_iter, key=sk)
 
        return render('admin/users_groups/users_groups.html')
 

	
 
    @HasPermissionAllDecorator('hg.admin')
 
    def create(self):
 
        """POST /users_groups: Create a new item"""
 
        # url('users_groups')
 

	
 
        users_group_form = UserGroupForm()()
 
        try:
 
            form_result = users_group_form.to_python(dict(request.POST))
 
            UserGroupModel().create(name=form_result['users_group_name'],
 
                                    owner=self.rhodecode_user.user_id,
 
                                    active=form_result['users_group_active'])
 

	
 
            gr = form_result['users_group_name']
 
            action_logger(self.rhodecode_user,
 
                          'admin_created_users_group:%s' % gr,
 
                          None, self.ip_addr, self.sa)
 
            h.flash(_('Created user group %s') % gr, category='success')
 
            Session().commit()
 
        except formencode.Invalid, errors:
 
            return htmlfill.render(
 
                render('admin/users_groups/users_group_add.html'),
 
                defaults=errors.value,
 
                errors=errors.error_dict or {},
 
                prefix_error=False,
 
                encoding="UTF-8")
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('Error occurred during creation of user group %s') \
 
                    % request.POST.get('users_group_name'), category='error')
 

	
 
        return redirect(url('users_groups'))
 

	
 
    @HasPermissionAllDecorator('hg.admin')
 
    def new(self, format='html'):
 
        """GET /users_groups/new: Form to create a new item"""
 
        # url('new_users_group')
 
        return render('admin/users_groups/users_group_add.html')
 

	
 
    @HasUserGroupPermissionAnyDecorator('usergroup.admin')
 
    def update(self, id):
 
        """PUT /users_groups/id: Update an existing item"""
 
        # Forms posted to this method should contain a hidden field:
 
        #    <input type="hidden" name="_method" value="PUT" />
 
        # Or using helpers:
 
        #    h.form(url('users_group', id=ID),
 
        #           method='put')
 
        # url('users_group', id=ID)
 

	
 
        c.users_group = UserGroup.get_or_404(id)
 
        self.__load_data(id)
 

	
 
        available_members = [safe_unicode(x[0]) for x in c.available_members]
 

	
 
        users_group_form = UserGroupForm(edit=True,
 
                                          old_data=c.users_group.get_dict(),
 
                                          available_members=available_members)()
 

	
 
        try:
 
            form_result = users_group_form.to_python(request.POST)
 
            UserGroupModel().update(c.users_group, form_result)
 
            gr = form_result['users_group_name']
 
            action_logger(self.rhodecode_user,
 
                          'admin_updated_users_group:%s' % gr,
 
                          None, self.ip_addr, self.sa)
 
            h.flash(_('Updated user group %s') % gr, category='success')
 
            Session().commit()
 
        except formencode.Invalid, errors:
 
            ug_model = UserGroupModel()
 
            defaults = errors.value
 
            e = errors.error_dict or {}
 
            defaults.update({
 
                'create_repo_perm': ug_model.has_perm(id,
 
                                                      'hg.create.repository'),
 
                'fork_repo_perm': ug_model.has_perm(id,
 
                                                    'hg.fork.repository'),
 
                '_method': 'put'
 
            })
 

	
 
            return htmlfill.render(
 
                render('admin/users_groups/users_group_edit.html'),
 
                defaults=defaults,
 
                errors=e,
 
                prefix_error=False,
 
                encoding="UTF-8")
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('Error occurred during update of user group %s') \
 
                    % request.POST.get('users_group_name'), category='error')
 

	
 
        return redirect(url('edit_users_group', id=id))
 

	
 
    @HasUserGroupPermissionAnyDecorator('usergroup.admin')
 
    def delete(self, id):
 
        """DELETE /users_groups/id: Delete an existing item"""
 
        # Forms posted to this method should contain a hidden field:
 
        #    <input type="hidden" name="_method" value="DELETE" />
 
        # Or using helpers:
 
        #    h.form(url('users_group', id=ID),
 
        #           method='delete')
 
        # url('users_group', id=ID)
 
        usr_gr = UserGroup.get_or_404(id)
 
        try:
 
            UserGroupModel().delete(usr_gr)
 
            Session().commit()
 
            h.flash(_('Successfully deleted user group'), category='success')
 
        except UserGroupsAssignedException, e:
 
            h.flash(e, category='error')
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('An error occurred during deletion of user group'),
 
                    category='error')
 
        return redirect(url('users_groups'))
 

	
 
    @HasUserGroupPermissionAnyDecorator('usergroup.admin')
 
    def set_user_group_perm_member(self, id):
 
        """
 
        grant permission for given usergroup
 

	
 
        :param id:
 
        """
 
        user_group = UserGroup.get_or_404(id)
 
        form = UserGroupPermsForm()().to_python(request.POST)
 

	
 
        # set the permissions !
 
        UserGroupModel()._update_permissions(user_group, form['perms_new'],
 
                                            form['perms_updates'])
 
        #TODO: implement this
 
        #action_logger(self.rhodecode_user, 'admin_changed_repo_permissions',
 
        #              repo_name, self.ip_addr, self.sa)
 
        Session().commit()
 
        h.flash(_('User Group permissions updated'), category='success')
 
        return redirect(url('edit_users_group', id=id))
 

	
 
    @HasUserGroupPermissionAnyDecorator('usergroup.admin')
 
    def delete_user_group_perm_member(self, id):
 
        """
 
        DELETE an existing repository group permission user
 

	
 
        :param group_name:
 
        """
 
        try:
 
            obj_type = request.POST.get('obj_type')
 
            obj_id = None
 
            if obj_type == 'user':
 
                obj_id = safe_int(request.POST.get('user_id'))
 
            elif obj_type == 'user_group':
 
                obj_id = safe_int(request.POST.get('user_group_id'))
 

	
 
            if not c.rhodecode_user.is_admin:
 
                if obj_type == 'user' and c.rhodecode_user.user_id == obj_id:
 
                    msg = _('Cannot revoke permission for yourself as admin')
 
                    h.flash(msg, category='warning')
 
                    raise Exception('revoke admin permission on self')
 
            if obj_type == 'user':
 
                UserGroupModel().revoke_user_permission(user_group=id,
 
                                                        user=obj_id)
 
            elif obj_type == 'user_group':
 
                pass
 
            Session().commit()
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('An error occurred during deletion of group user'),
 
            h.flash(_('An error occurred during revoking of permission'),
 
                    category='error')
 
            raise HTTPInternalServerError()
 

	
 
    def show(self, id, format='html'):
 
        """GET /users_groups/id: Show a specific item"""
 
        # url('users_group', id=ID)
 

	
 
    @HasUserGroupPermissionAnyDecorator('usergroup.admin')
 
    def edit(self, id, format='html'):
 
        """GET /users_groups/id/edit: Form to edit an existing item"""
 
        # url('edit_users_group', id=ID)
 

	
 
        c.users_group = UserGroup.get_or_404(id)
 
        self.__load_data(id)
 

	
 
        defaults = self.__load_defaults(id)
 

	
 
        return htmlfill.render(
 
            render('admin/users_groups/users_group_edit.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False
 
        )
 

	
 
    @HasUserGroupPermissionAnyDecorator('usergroup.admin')
 
    def update_perm(self, id):
 
        """PUT /users_perm/id: Update an existing item"""
 
        # url('users_group_perm', id=ID, method='put')
 

	
 
        users_group = UserGroup.get_or_404(id)
 
        grant_create_perm = str2bool(request.POST.get('create_repo_perm'))
 
        grant_fork_perm = str2bool(request.POST.get('fork_repo_perm'))
 
        inherit_perms = str2bool(request.POST.get('inherit_default_permissions'))
 

	
 
        usergroup_model = UserGroupModel()
 

	
 
        try:
 
            users_group.inherit_default_permissions = inherit_perms
 
            Session().add(users_group)
 

	
 
            if grant_create_perm:
 
                usergroup_model.revoke_perm(id, 'hg.create.none')
 
                usergroup_model.grant_perm(id, 'hg.create.repository')
 
                h.flash(_("Granted 'repository create' permission to user group"),
 
                        category='success')
 
            else:
 
                usergroup_model.revoke_perm(id, 'hg.create.repository')
 
                usergroup_model.grant_perm(id, 'hg.create.none')
 
                h.flash(_("Revoked 'repository create' permission to user group"),
 
                        category='success')
 

	
 
            if grant_fork_perm:
 
                usergroup_model.revoke_perm(id, 'hg.fork.none')
 
                usergroup_model.grant_perm(id, 'hg.fork.repository')
 
                h.flash(_("Granted 'repository fork' permission to user group"),
 
                        category='success')
 
            else:
 
                usergroup_model.revoke_perm(id, 'hg.fork.repository')
 
                usergroup_model.grant_perm(id, 'hg.fork.none')
 
                h.flash(_("Revoked 'repository fork' permission to user group"),
 
                        category='success')
 

	
 
            Session().commit()
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('An error occurred during permissions saving'),
 
                    category='error')
 

	
 
        return redirect(url('edit_users_group', id=id))
rhodecode/public/js/rhodecode.js
Show inline comments
 
/**
 
RhodeCode JS Files
 
**/
 

	
 
if (typeof console == "undefined" || typeof console.log == "undefined"){
 
    console = { log: function() {} }
 
}
 

	
 

	
 
var str_repeat = function(i, m) {
 
    for (var o = []; m > 0; o[--m] = i);
 
    return o.join('');
 
};
 

	
 
/**
 
 * INJECT .format function into String
 
 * Usage: "My name is {0} {1}".format("Johny","Bravo")
 
 * Return "My name is Johny Bravo"
 
 * Inspired by https://gist.github.com/1049426
 
 */
 
String.prototype.format = function() {
 

	
 
      function format() {
 
        var str = this;
 
        var len = arguments.length+1;
 
        var safe = undefined;
 
        var arg = undefined;
 

	
 
        // For each {0} {1} {n...} replace with the argument in that position.  If
 
        // the argument is an object or an array it will be stringified to JSON.
 
        for (var i=0; i < len; arg = arguments[i++]) {
 
          safe = typeof arg === 'object' ? JSON.stringify(arg) : arg;
 
          str = str.replace(RegExp('\\{'+(i-1)+'\\}', 'g'), safe);
 
        }
 
        return str;
 
      }
 

	
 
      // Save a reference of what may already exist under the property native.
 
      // Allows for doing something like: if("".format.native) { /* use native */ }
 
      format.native = String.prototype.format;
 

	
 
      // Replace the prototype property
 
      return format;
 

	
 
}();
 

	
 
String.prototype.strip = function(char) {
 
    if(char === undefined){
 
        char = '\\s';
 
    }
 
    return this.replace(new RegExp('^'+char+'+|'+char+'+$','g'), '');
 
}
 
String.prototype.lstrip = function(char) {
 
    if(char === undefined){
 
        char = '\\s';
 
    }
 
    return this.replace(new RegExp('^'+char+'+'),'');
 
}
 
String.prototype.rstrip = function(char) {
 
    if(char === undefined){
 
        char = '\\s';
 
    }
 
    return this.replace(new RegExp(''+char+'+$'),'');
 
}
 

	
 

	
 
if(!Array.prototype.indexOf) {
 
    Array.prototype.indexOf = function(needle) {
 
        for(var i = 0; i < this.length; i++) {
 
            if(this[i] === needle) {
 
                return i;
 
            }
 
        }
 
        return -1;
 
    };
 
}
 

	
 
// IE(CRAP) doesn't support previousElementSibling
 
var prevElementSibling = function( el ) {
 
    if( el.previousElementSibling ) {
 
        return el.previousElementSibling;
 
    } else {
 
        while( el = el.previousSibling ) {
 
            if( el.nodeType === 1 ) return el;
 
        }
 
    }
 
}
 

	
 
/**
 
 * SmartColorGenerator
 
 *
 
 *usage::
 
 *  var CG = new ColorGenerator();
 
 *  var col = CG.getColor(key); //returns array of RGB
 
 *  'rgb({0})'.format(col.join(',')
 
 *
 
 * @returns {ColorGenerator}
 
 */
 
var ColorGenerator = function(){
 
    this.GOLDEN_RATIO = 0.618033988749895;
 
    this.CURRENT_RATIO = 0.22717784590367374 // this can be random
 
    this.HSV_1 = 0.75;//saturation
 
    this.HSV_2 = 0.95;
 
    this.color;
 
    this.cacheColorMap = {};
 
};
 

	
 
ColorGenerator.prototype = {
 
    getColor:function(key){
 
        if(this.cacheColorMap[key] !== undefined){
 
            return this.cacheColorMap[key];
 
        }
 
        else{
 
            this.cacheColorMap[key] = this.generateColor();
 
            return this.cacheColorMap[key];
 
        }
 
    },
 
    _hsvToRgb:function(h,s,v){
 
        if (s == 0.0)
 
            return [v, v, v];
 
        i = parseInt(h * 6.0)
 
        f = (h * 6.0) - i
 
        p = v * (1.0 - s)
 
        q = v * (1.0 - s * f)
 
        t = v * (1.0 - s * (1.0 - f))
 
        i = i % 6
 
        if (i == 0)
 
            return [v, t, p]
 
        if (i == 1)
 
            return [q, v, p]
 
        if (i == 2)
 
            return [p, v, t]
 
        if (i == 3)
 
            return [p, q, v]
 
        if (i == 4)
 
            return [t, p, v]
 
        if (i == 5)
 
            return [v, p, q]
 
    },
 
    generateColor:function(){
 
        this.CURRENT_RATIO = this.CURRENT_RATIO+this.GOLDEN_RATIO;
 
        this.CURRENT_RATIO = this.CURRENT_RATIO %= 1;
 
        HSV_tuple = [this.CURRENT_RATIO, this.HSV_1, this.HSV_2]
 
        RGB_tuple = this._hsvToRgb(HSV_tuple[0],HSV_tuple[1],HSV_tuple[2]);
 
        function toRgb(v){
 
            return ""+parseInt(v*256)
 
        }
 
        return [toRgb(RGB_tuple[0]),toRgb(RGB_tuple[1]),toRgb(RGB_tuple[2])];
 

	
 
    }
 
}
 

	
 
/**
 
 * PyRoutesJS
 
 *
 
 * Usage pyroutes.url('mark_error_fixed',{"error_id":error_id}) // /mark_error_fixed/<error_id>
 
 */
 
var pyroutes = (function() {
 
    // access global map defined in special file pyroutes
 
    var matchlist = PROUTES_MAP;
 
    var sprintf = (function() {
 
        function get_type(variable) {
 
            return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
 
        }
 
        function str_repeat(input, multiplier) {
 
            for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */}
 
            return output.join('');
 
        }
 

	
 
        var str_format = function() {
 
            if (!str_format.cache.hasOwnProperty(arguments[0])) {
 
                str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
 
            }
 
            return str_format.format.call(null, str_format.cache[arguments[0]], arguments);
 
        };
 

	
 
        str_format.format = function(parse_tree, argv) {
 
            var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
 
            for (i = 0; i < tree_length; i++) {
 
                node_type = get_type(parse_tree[i]);
 
                if (node_type === 'string') {
 
                    output.push(parse_tree[i]);
 
                }
 
                else if (node_type === 'array') {
 
                    match = parse_tree[i]; // convenience purposes only
 
                    if (match[2]) { // keyword argument
 
                        arg = argv[cursor];
 
                        for (k = 0; k < match[2].length; k++) {
 
                            if (!arg.hasOwnProperty(match[2][k])) {
 
                                throw(sprintf('[sprintf] property "%s" does not exist', match[2][k]));
 
                            }
 
                            arg = arg[match[2][k]];
 
                        }
 
                    }
 
                    else if (match[1]) { // positional argument (explicit)
 
                        arg = argv[match[1]];
 
                    }
 
                    else { // positional argument (implicit)
 
                        arg = argv[cursor++];
 
                    }
 

	
 
                    if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
 
                        throw(sprintf('[sprintf] expecting number but found %s', get_type(arg)));
 
                    }
 
                    switch (match[8]) {
 
                        case 'b': arg = arg.toString(2); break;
 
                        case 'c': arg = String.fromCharCode(arg); break;
 
                        case 'd': arg = parseInt(arg, 10); break;
 
                        case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
 
                        case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
 
                        case 'o': arg = arg.toString(8); break;
 
                        case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
 
                        case 'u': arg = Math.abs(arg); break;
 
                        case 'x': arg = arg.toString(16); break;
 
                        case 'X': arg = arg.toString(16).toUpperCase(); break;
 
                    }
 
                    arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
 
                    pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
 
                    pad_length = match[6] - String(arg).length;
 
                    pad = match[6] ? str_repeat(pad_character, pad_length) : '';
 
                    output.push(match[5] ? arg + pad : pad + arg);
 
                }
 
            }
 
            return output.join('');
 
        };
 

	
 
        str_format.cache = {};
 

	
 
        str_format.parse = function(fmt) {
 
            var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
 
            while (_fmt) {
 
                if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
 
                    parse_tree.push(match[0]);
 
                }
 
                else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
 
                    parse_tree.push('%');
 
                }
 
                else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
 
                    if (match[2]) {
 
                        arg_names |= 1;
 
                        var field_list = [], replacement_field = match[2], field_match = [];
 
                        if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
 
                            field_list.push(field_match[1]);
 
                            while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
 
                                if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
 
                                    field_list.push(field_match[1]);
 
                                }
 
                                else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
 
                                    field_list.push(field_match[1]);
 
                                }
 
                                else {
 
                                    throw('[sprintf] huh?');
 
                                }
 
                            }
 
                        }
 
                        else {
 
                            throw('[sprintf] huh?');
 
                        }
 
                        match[2] = field_list;
 
                    }
 
                    else {
 
                        arg_names |= 2;
 
                    }
 
                    if (arg_names === 3) {
 
                        throw('[sprintf] mixing positional and named placeholders is not (yet) supported');
 
                    }
 
                    parse_tree.push(match);
 
                }
 
                else {
 
                    throw('[sprintf] huh?');
 
                }
 
                _fmt = _fmt.substring(match[0].length);
 
            }
 
            return parse_tree;
 
        };
 

	
 
        return str_format;
 
    })();
 

	
 
    var vsprintf = function(fmt, argv) {
 
        argv.unshift(fmt);
 
        return sprintf.apply(null, argv);
 
    };
 
    return {
 
        'url': function(route_name, params) {
 
            var result = route_name;
 
            if (typeof(params) != 'object'){
 
                params = {};
 
            }
 
            if (matchlist.hasOwnProperty(route_name)) {
 
                var route = matchlist[route_name];
 
                // param substitution
 
                for(var i=0; i < route[1].length; i++) {
 

	
 
                   if (!params.hasOwnProperty(route[1][i]))
 
                        throw new Error(route[1][i] + ' missing in "' + route_name + '" route generation');
 
                }
 
                result = sprintf(route[0], params);
 

	
 
                var ret = [];
 
                //extra params => GET
 
                for(param in params){
 
                    if (route[1].indexOf(param) == -1){
 
                        ret.push(encodeURIComponent(param) + "=" + encodeURIComponent(params[param]));
 
                    }
 
                }
 
                var _parts = ret.join("&");
 
                if(_parts){
 
                    result = result +'?'+ _parts
 
                }
 
            }
 

	
 
            return result;
 
        },
 
        'register': function(route_name, route_tmpl, req_params) {
 
            if (typeof(req_params) != 'object') {
 
                req_params = [];
 
            }
 
            //fix escape
 
            route_tmpl = unescape(route_tmpl);
 
            keys = [];
 
            for (o in req_params){
 
                keys.push(req_params[o])
 
            }
 
            matchlist[route_name] = [
 
                route_tmpl,
 
                keys
 
            ]
 
        },
 
        '_routes': function(){
 
            return matchlist;
 
        }
 
    }
 
})();
 

	
 

	
 

	
 
/**
 
 * GLOBAL YUI Shortcuts
 
 */
 
var YUC = YAHOO.util.Connect;
 
var YUD = YAHOO.util.Dom;
 
var YUE = YAHOO.util.Event;
 
var YUQ = YAHOO.util.Selector.query;
 

	
 
// defines if push state is enabled for this browser ?
 
var push_state_enabled = Boolean(
 
        window.history && window.history.pushState && window.history.replaceState
 
        && !(   /* disable for versions of iOS before version 4.3 (8F190) */
 
                (/ Mobile\/([1-7][a-z]|(8([abcde]|f(1[0-8]))))/i).test(navigator.userAgent)
 
                /* disable for the mercury iOS browser, or at least older versions of the webkit engine */
 
                || (/AppleWebKit\/5([0-2]|3[0-2])/i).test(navigator.userAgent)
 
        )
 
);
 

	
 
var _run_callbacks = function(callbacks){
 
    if (callbacks !== undefined){
 
        var _l = callbacks.length;
 
        for (var i=0;i<_l;i++){
 
            var func = callbacks[i];
 
            if(typeof(func)=='function'){
 
                try{
 
                    func();
 
                }catch (err){};
 
            }
 
        }
 
    }
 
}
 

	
 
/**
 
 * turns objects into GET query string
 
 */
 
var toQueryString = function(o) {
 
    if(typeof o !== 'object') {
 
        return false;
 
    }
 
    var _p, _qs = [];
 
    for(_p in o) {
 
        _qs.push(encodeURIComponent(_p) + '=' + encodeURIComponent(o[_p]));
 
    }
 
    return _qs.join('&');
 
};
 

	
 
/**
 
 * Partial Ajax Implementation
 
 *
 
 * @param url: defines url to make partial request
 
 * @param container: defines id of container to input partial result
 
 * @param s_call: success callback function that takes o as arg
 
 *  o.tId
 
 *  o.status
 
 *  o.statusText
 
 *  o.getResponseHeader[ ]
 
 *  o.getAllResponseHeaders
 
 *  o.responseText
 
 *  o.responseXML
 
 *  o.argument
 
 * @param f_call: failure callback
 
 * @param args arguments
 
 */
 
function ypjax(url,container,s_call,f_call,args){
 
    var method='GET';
 
    if(args===undefined){
 
        args=null;
 
    }
 

	
 
    // Set special header for partial ajax == HTTP_X_PARTIAL_XHR
 
    YUC.initHeader('X-PARTIAL-XHR',true);
 

	
 
    // wrapper of passed callback
 
    var s_wrapper = (function(o){
 
        return function(o){
 
            YUD.get(container).innerHTML=o.responseText;
 
            YUD.setStyle(container,'opacity','1.0');
 
            //execute the given original callback
 
            if (s_call !== undefined){
 
                s_call(o);
 
            }
 
        }
 
    })()
 
    YUD.setStyle(container,'opacity','0.3');
 
    YUC.asyncRequest(method,url,{
 
        success:s_wrapper,
 
        failure:function(o){
 
            console.log(o);
 
            YUD.get(container).innerHTML='<span class="error_red">ERROR: {0}</span>'.format(o.status);
 
            YUD.setStyle(container,'opacity','1.0');
 
        },
 
        cache:false
 
    },args);
 

	
 
};
 

	
 
var ajaxGET = function(url,success) {
 
    // Set special header for ajax == HTTP_X_PARTIAL_XHR
 
    YUC.initHeader('X-PARTIAL-XHR',true);
 

	
 
    var sUrl = url;
 
    var callback = {
 
        success: success,
 
        failure: function (o) {
 
            if (o.status != 0) {
 
                alert("error: " + o.statusText);
 
            };
 
        },
 
    };
 

	
 
    var request = YAHOO.util.Connect.asyncRequest('GET', sUrl, callback);
 
    return request;
 
};
 

	
 

	
 

	
 
var ajaxPOST = function(url,postData,success) {
 
    // Set special header for ajax == HTTP_X_PARTIAL_XHR
 
    YUC.initHeader('X-PARTIAL-XHR',true);
 

	
 
    var toQueryString = function(o) {
 
        if(typeof o !== 'object') {
 
            return false;
 
        }
 
        var _p, _qs = [];
 
        for(_p in o) {
 
            _qs.push(encodeURIComponent(_p) + '=' + encodeURIComponent(o[_p]));
 
        }
 
        return _qs.join('&');
 
    };
 

	
 
    var sUrl = url;
 
    var callback = {
 
        success: success,
 
        failure: function (o) {
 
            alert("error");
 
        },
 
    };
 
    var postData = toQueryString(postData);
 
    var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
 
    return request;
 
};
 

	
 

	
 
/**
 
 * tooltip activate
 
 */
 
var tooltip_activate = function(){
 
    yt = YAHOO.yuitip.main;
 
    YUE.onDOMReady(yt.init);
 
};
 

	
 
/**
 
 * show more
 
 */
 
var show_more_event = function(){
 
    YUE.on(YUD.getElementsByClassName('show_more'),'click',function(e){
 
        var el = e.target;
 
        YUD.setStyle(YUD.get(el.id.substring(1)),'display','');
 
        YUD.setStyle(el.parentNode,'display','none');
 
    });
 
};
 

	
 
/**
 
 * show changeset tooltip
 
 */
 
var show_changeset_tooltip = function(){
 
    YUE.on(YUD.getElementsByClassName('lazy-cs'), 'mouseover', function(e){
 
        var target = e.currentTarget;
 
        var rid = YUD.getAttribute(target,'raw_id');
 
        var repo_name = YUD.getAttribute(target,'repo_name');
 
        var ttid = 'tt-'+rid;
 
        var success = function(o){
 
            var json = JSON.parse(o.responseText);
 
            YUD.addClass(target,'tooltip')
 
            YUD.setAttribute(target, 'title',json['message']);
 
            YAHOO.yuitip.main.show_yuitip(e, target);
 
        }
 
        if(rid && !YUD.hasClass(target, 'tooltip')){
 
            YUD.setAttribute(target,'id',ttid);
 
            YUD.setAttribute(target, 'title',_TM['loading...']);
 
            YAHOO.yuitip.main.set_listeners(target);
 
            YAHOO.yuitip.main.show_yuitip(e, target);
 
            var url = pyroutes.url('changeset_info', {"repo_name":repo_name, "revision": rid});
 
            ajaxGET(url, success)
 
        }
 
    });
 
};
 

	
 
var onSuccessFollow = function(target){
 
    var f = YUD.get(target);
 
    var f_cnt = YUD.get('current_followers_count');
 

	
 
    if(YUD.hasClass(f, 'follow')){
 
        f.setAttribute('class','following');
 
        f.setAttribute('title',_TM['Stop following this repository']);
 

	
 
        if(f_cnt){
 
            var cnt = Number(f_cnt.innerHTML)+1;
 
            f_cnt.innerHTML = cnt;
 
        }
 
    }
 
    else{
 
        f.setAttribute('class','follow');
 
        f.setAttribute('title',_TM['Start following this repository']);
 
        if(f_cnt){
 
            var cnt = Number(f_cnt.innerHTML)-1;
 
            f_cnt.innerHTML = cnt;
 
        }
 
    }
 
}
 

	
 
var toggleFollowingUser = function(target,fallows_user_id,token,user_id){
 
    args = 'follows_user_id='+fallows_user_id;
 
    args+= '&amp;auth_token='+token;
 
    if(user_id != undefined){
 
        args+="&amp;user_id="+user_id;
 
    }
 
    YUC.asyncRequest('POST',TOGGLE_FOLLOW_URL,{
 
        success:function(o){
 
            onSuccessFollow(target);
 
        }
 
    },args);
 
    return false;
 
}
 

	
 
var toggleFollowingRepo = function(target,fallows_repo_id,token,user_id){
 

	
 
    args = 'follows_repo_id='+fallows_repo_id;
 
    args+= '&amp;auth_token='+token;
 
    if(user_id != undefined){
 
        args+="&amp;user_id="+user_id;
 
    }
 
    YUC.asyncRequest('POST',TOGGLE_FOLLOW_URL,{
 
        success:function(o){
 
            onSuccessFollow(target);
 
        }
 
    },args);
 
    return false;
 
}
 

	
 
var showRepoSize = function(target, repo_name, token){
 
    var args= 'auth_token='+token;
 

	
 
    if(!YUD.hasClass(target, 'loaded')){
 
        YUD.get(target).innerHTML = _TM['Loading ...'];
 
        var url = pyroutes.url('repo_size', {"repo_name":repo_name});
 
        YUC.asyncRequest('POST',url,{
 
            success:function(o){
 
                YUD.get(target).innerHTML = JSON.parse(o.responseText);
 
                YUD.addClass(target, 'loaded');
 
            }
 
        },args);
 
    }
 
    return false;
 
}
 

	
 
/**
 
 * TOOLTIP IMPL.
 
 */
 
YAHOO.namespace('yuitip');
 
YAHOO.yuitip.main = {
 

	
 
    $:          YAHOO.util.Dom.get,
 

	
 
    bgColor:    '#000',
 
    speed:      0.3,
 
    opacity:    0.9,
 
    offset:     [15,15],
 
    useAnim:    false,
 
    maxWidth:   600,
 
    add_links:  false,
 
    yuitips:    [],
 

	
 
    set_listeners: function(tt){
 
        YUE.on(tt, 'mouseover', yt.show_yuitip,  tt);
 
        YUE.on(tt, 'mousemove', yt.move_yuitip,  tt);
 
        YUE.on(tt, 'mouseout',  yt.close_yuitip, tt);
 
    },
 

	
 
    init: function(){
 
        yt.tipBox = yt.$('tip-box');
 
        if(!yt.tipBox){
 
            yt.tipBox = document.createElement('div');
 
            document.body.appendChild(yt.tipBox);
 
            yt.tipBox.id = 'tip-box';
 
        }
 

	
 
        YUD.setStyle(yt.tipBox, 'display', 'none');
 
        YUD.setStyle(yt.tipBox, 'position', 'absolute');
 
        if(yt.maxWidth !== null){
 
            YUD.setStyle(yt.tipBox, 'max-width', yt.maxWidth+'px');
 
        }
 

	
 
        var yuitips = YUD.getElementsByClassName('tooltip');
 

	
 
        if(yt.add_links === true){
 
            var links = document.getElementsByTagName('a');
 
            var linkLen = links.length;
 
            for(i=0;i<linkLen;i++){
 
                yuitips.push(links[i]);
 
            }
 
        }
 

	
 
        var yuiLen = yuitips.length;
 

	
 
        for(i=0;i<yuiLen;i++){
 
            yt.set_listeners(yuitips[i]);
 
        }
 
    },
 

	
 
    show_yuitip: function(e, el){
 
        YUE.stopEvent(e);
 
        if(el.tagName.toLowerCase() === 'img'){
 
            yt.tipText = el.alt ? el.alt : '';
 
        } else {
 
            yt.tipText = el.title ? el.title : '';
 
        }
 

	
 
        if(yt.tipText !== ''){
 
            // save org title
 
            YUD.setAttribute(el, 'tt_title', yt.tipText);
 
            // reset title to not show org tooltips
 
            YUD.setAttribute(el, 'title', '');
 

	
 
            yt.tipBox.innerHTML = yt.tipText;
 
            YUD.setStyle(yt.tipBox, 'display', 'block');
 
            if(yt.useAnim === true){
 
                YUD.setStyle(yt.tipBox, 'opacity', '0');
 
                var newAnim = new YAHOO.util.Anim(yt.tipBox,
 
                    {
 
                        opacity: { to: yt.opacity }
 
                    }, yt.speed, YAHOO.util.Easing.easeOut
 
                );
 
                newAnim.animate();
 
            }
 
        }
 
    },
 

	
 
    move_yuitip: function(e, el){
 
        YUE.stopEvent(e);
 
        var movePos = YUE.getXY(e);
 
        YUD.setStyle(yt.tipBox, 'top', (movePos[1] + yt.offset[1]) + 'px');
 
        YUD.setStyle(yt.tipBox, 'left', (movePos[0] + yt.offset[0]) + 'px');
 
    },
 

	
 
    close_yuitip: function(e, el){
 
        YUE.stopEvent(e);
 

	
 
        if(yt.useAnim === true){
 
            var newAnim = new YAHOO.util.Anim(yt.tipBox,
 
                {
 
                    opacity: { to: 0 }
 
                }, yt.speed, YAHOO.util.Easing.easeOut
 
            );
 
            newAnim.animate();
 
        } else {
 
            YUD.setStyle(yt.tipBox, 'display', 'none');
 
        }
 
        YUD.setAttribute(el,'title', YUD.getAttribute(el, 'tt_title'));
 
    }
 
}
 

	
 
/**
 
 * Quick filter widget
 
 *
 
 * @param target: filter input target
 
 * @param nodes: list of nodes in html we want to filter.
 
 * @param display_element function that takes current node from nodes and
 
 *    does hide or show based on the node
 
 *
 
 */
 
var q_filter = function(target,nodes,display_element){
 

	
 
    var nodes = nodes;
 
    var q_filter_field = YUD.get(target);
 
    var F = YAHOO.namespace(target);
 

	
 
    YUE.on(q_filter_field,'keyup',function(e){
 
        clearTimeout(F.filterTimeout);
 
        F.filterTimeout = setTimeout(F.updateFilter,600);
 
    });
 

	
 
    F.filterTimeout = null;
 

	
 
    var show_node = function(node){
 
        YUD.setStyle(node,'display','')
 
    }
 
    var hide_node = function(node){
 
        YUD.setStyle(node,'display','none');
 
    }
 

	
 
    F.updateFilter  = function() {
 
       // Reset timeout
 
       F.filterTimeout = null;
 

	
 
       var obsolete = [];
 

	
 
       var req = q_filter_field.value.toLowerCase();
 

	
 
       var l = nodes.length;
 
       var i;
 
       var showing = 0;
 

	
 
       for (i=0;i<l;i++ ){
 
           var n = nodes[i];
 
           var target_element = display_element(n)
 
           if(req && n.innerHTML.toLowerCase().indexOf(req) == -1){
 
               hide_node(target_element);
 
           }
 
           else{
 
               show_node(target_element);
 
               showing+=1;
 
           }
 
       }
 

	
 
       // if repo_count is set update the number
 
       var cnt = YUD.get('repo_count');
 
       if(cnt){
 
           YUD.get('repo_count').innerHTML = showing;
 
       }
 

	
 
    }
 
};
 

	
 
var tableTr = function(cls, body){
 
    var _el = document.createElement('div');
 
    var cont = new YAHOO.util.Element(body);
 
    var comment_id = fromHTML(body).children[0].id.split('comment-')[1];
 
    var id = 'comment-tr-{0}'.format(comment_id);
 
    var _html = ('<table><tbody><tr id="{0}" class="{1}">'+
 
                  '<td class="lineno-inline new-inline"></td>'+
 
                  '<td class="lineno-inline old-inline"></td>'+
 
                  '<td>{2}</td>'+
 
                 '</tr></tbody></table>').format(id, cls, body);
 
    _el.innerHTML = _html;
 
    return _el.children[0].children[0].children[0];
 
};
 

	
 
/** comments **/
 
var removeInlineForm = function(form) {
 
    form.parentNode.removeChild(form);
 
};
 

	
 
var createInlineForm = function(parent_tr, f_path, line) {
 
    var tmpl = YUD.get('comment-inline-form-template').innerHTML;
 
    tmpl = tmpl.format(f_path, line);
 
    var form = tableTr('comment-form-inline',tmpl)
 

	
 
    // create event for hide button
 
    form = new YAHOO.util.Element(form);
 
    var form_hide_button = new YAHOO.util.Element(YUD.getElementsByClassName('hide-inline-form',null,form)[0]);
 
    form_hide_button.on('click', function(e) {
 
        var newtr = e.currentTarget.parentNode.parentNode.parentNode.parentNode.parentNode;
 
        if(YUD.hasClass(newtr.nextElementSibling,'inline-comments-button')){
 
            YUD.setStyle(newtr.nextElementSibling,'display','');
 
        }
 
        removeInlineForm(newtr);
 
        YUD.removeClass(parent_tr, 'form-open');
 
        YUD.removeClass(parent_tr, 'hl-comment');
 

	
 
    });
 

	
 
    return form
 
};
 

	
 
/**
 
 * Inject inline comment for on given TR this tr should be always an .line
 
 * tr containing the line. Code will detect comment, and always put the comment
 
 * block at the very bottom
 
 */
 
var injectInlineForm = function(tr){
 
      if(!YUD.hasClass(tr, 'line')){
 
          return
 
      }
 
      var submit_url = AJAX_COMMENT_URL;
 
      var _td = YUD.getElementsByClassName('code',null,tr)[0];
 
      if(YUD.hasClass(tr,'form-open') || YUD.hasClass(tr,'context') || YUD.hasClass(_td,'no-comment')){
 
          return
 
      }
 
      YUD.addClass(tr,'form-open');
 
      YUD.addClass(tr,'hl-comment');
 
      var node = YUD.getElementsByClassName('full_f_path',null,tr.parentNode.parentNode.parentNode)[0];
 
      var f_path = YUD.getAttribute(node,'path');
 
      var lineno = getLineNo(tr);
 
      var form = createInlineForm(tr, f_path, lineno, submit_url);
 

	
 
      var parent = tr;
 
      while (1){
 
          var n = parent.nextElementSibling;
 
          // next element are comments !
 
          if(YUD.hasClass(n,'inline-comments')){
 
              parent = n;
 
          }
 
          else{
 
              break;
 
          }
 
      }
 
      YUD.insertAfter(form,parent);
 
      var f = YUD.get(form);
 
      var overlay = YUD.getElementsByClassName('overlay',null,f)[0];
 
      var _form = YUD.getElementsByClassName('inline-form',null,f)[0];
 

	
 
      YUE.on(YUD.get(_form), 'submit',function(e){
 
          YUE.preventDefault(e);
 

	
 
          //ajax submit
 
          var text = YUD.get('text_'+lineno).value;
 
          var postData = {
 
                'text':text,
 
                'f_path':f_path,
 
                'line':lineno
 
          };
 

	
 
          if(lineno === undefined){
 
@@ -1654,542 +1657,573 @@ var MentionsAutoComplete = function (div
 
        ownerAC: ownerAC,
 
    };
 
}
 

	
 
var addReviewMember = function(id,fname,lname,nname,gravatar_link){
 
    var members  = YUD.get('review_members');
 
    var tmpl = '<li id="reviewer_{2}">'+
 
    '<div class="reviewers_member">'+
 
      '<div class="gravatar"><img alt="gravatar" src="{0}"/> </div>'+
 
      '<div style="float:left">{1}</div>'+
 
      '<input type="hidden" value="{2}" name="review_members" />'+
 
      '<span class="delete_icon action_button" onclick="removeReviewMember({2})"></span>'+
 
    '</div>'+
 
    '</li>' ;
 
    var displayname = "{0} {1} ({2})".format(fname,lname,nname);
 
    var element = tmpl.format(gravatar_link,displayname,id);
 
    // check if we don't have this ID already in
 
    var ids = [];
 
    var _els = YUQ('#review_members li');
 
    for (el in _els){
 
        ids.push(_els[el].id)
 
    }
 
    if(ids.indexOf('reviewer_'+id) == -1){
 
        //only add if it's not there
 
        members.innerHTML += element;
 
    }
 

	
 
}
 

	
 
var removeReviewMember = function(reviewer_id, repo_name, pull_request_id){
 
    var el = YUD.get('reviewer_{0}'.format(reviewer_id));
 
    if (el.parentNode !== undefined){
 
        el.parentNode.removeChild(el);
 
    }
 
}
 

	
 
var updateReviewers = function(reviewers_ids, repo_name, pull_request_id){
 
    if (reviewers_ids === undefined){
 
      var reviewers_ids = [];
 
      var ids = YUQ('#review_members input');
 
      for(var i=0; i<ids.length;i++){
 
          var id = ids[i].value
 
          reviewers_ids.push(id);
 
      }
 
    }
 
    var url = pyroutes.url('pullrequest_update', {"repo_name":repo_name,
 
                                                  "pull_request_id": pull_request_id});
 
    var postData = {'_method':'put',
 
                    'reviewers_ids': reviewers_ids};
 
    var success = function(o){
 
        window.location.reload();
 
    }
 
    ajaxPOST(url,postData,success);
 
}
 

	
 
var PullRequestAutoComplete = function (divid, cont, users_list, groups_list) {
 
    var myUsers = users_list;
 
    var myGroups = groups_list;
 

	
 
    // Define a custom search function for the DataSource of users
 
    var matchUsers = function (sQuery) {
 
            // Case insensitive matching
 
            var query = sQuery.toLowerCase();
 
            var i = 0;
 
            var l = myUsers.length;
 
            var matches = [];
 

	
 
            // Match against each name of each contact
 
            for (; i < l; i++) {
 
                contact = myUsers[i];
 
                if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
 
                     ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
 
                     ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
 
                       matches[matches.length] = contact;
 
                   }
 
            }
 
            return matches;
 
        };
 

	
 
    // Define a custom search function for the DataSource of userGroups
 
    var matchGroups = function (sQuery) {
 
            // Case insensitive matching
 
            var query = sQuery.toLowerCase();
 
            var i = 0;
 
            var l = myGroups.length;
 
            var matches = [];
 

	
 
            // Match against each name of each contact
 
            for (; i < l; i++) {
 
                matched_group = myGroups[i];
 
                if (matched_group.grname.toLowerCase().indexOf(query) > -1) {
 
                    matches[matches.length] = matched_group;
 
                }
 
            }
 
            return matches;
 
        };
 

	
 
    //match all
 
    var matchAll = function (sQuery) {
 
            u = matchUsers(sQuery);
 
            return u
 
        };
 

	
 
    // DataScheme for owner
 
    var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
 

	
 
    ownerDS.responseSchema = {
 
        fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
 
    };
 

	
 
    // Instantiate AutoComplete for mentions
 
    var reviewerAC = new YAHOO.widget.AutoComplete(divid, cont, ownerDS);
 
    reviewerAC.useShadow = false;
 
    reviewerAC.resultTypeList = false;
 
    reviewerAC.suppressInputUpdate = true;
 
    reviewerAC.animVert = false;
 
    reviewerAC.animHoriz = false;
 
    reviewerAC.animSpeed = 0.1;
 

	
 
    // Helper highlight function for the formatter
 
    var highlightMatch = function (full, snippet, matchindex) {
 
            return full.substring(0, matchindex)
 
            + "<span class='match'>"
 
            + full.substr(matchindex, snippet.length)
 
            + "</span>" + full.substring(matchindex + snippet.length);
 
        };
 

	
 
    // Custom formatter to highlight the matching letters
 
    reviewerAC.formatResult = function (oResultData, sQuery, sResultMatch) {
 
            var org_sQuery = sQuery;
 
            if(this.dataSource.mentionQuery != null){
 
                sQuery = this.dataSource.mentionQuery;
 
            }
 

	
 
            var query = sQuery.toLowerCase();
 
            var _gravatar = function(res, em, group){
 
                if (group !== undefined){
 
                    em = '/images/icons/group.png'
 
                }
 
                tmpl = '<div class="ac-container-wrap"><img class="perm-gravatar-ac" src="{0}"/>{1}</div>'
 
                return tmpl.format(em,res)
 
            }
 
            if (oResultData.nname != undefined) {
 
                var fname = oResultData.fname || "";
 
                var lname = oResultData.lname || "";
 
                var nname = oResultData.nname;
 

	
 
                // Guard against null value
 
                var fnameMatchIndex = fname.toLowerCase().indexOf(query),
 
                    lnameMatchIndex = lname.toLowerCase().indexOf(query),
 
                    nnameMatchIndex = nname.toLowerCase().indexOf(query),
 
                    displayfname, displaylname, displaynname;
 

	
 
                if (fnameMatchIndex > -1) {
 
                    displayfname = highlightMatch(fname, query, fnameMatchIndex);
 
                } else {
 
                    displayfname = fname;
 
                }
 

	
 
                if (lnameMatchIndex > -1) {
 
                    displaylname = highlightMatch(lname, query, lnameMatchIndex);
 
                } else {
 
                    displaylname = lname;
 
                }
 

	
 
                if (nnameMatchIndex > -1) {
 
                    displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
 
                } else {
 
                    displaynname = nname ? "(" + nname + ")" : "";
 
                }
 

	
 
                return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
 
            } else {
 
                return '';
 
            }
 
        };
 

	
 
    //members cache to catch duplicates
 
    reviewerAC.dataSource.cache = [];
 
    // hack into select event
 
    if(reviewerAC.itemSelectEvent){
 
        reviewerAC.itemSelectEvent.subscribe(function (sType, aArgs) {
 

	
 
            var myAC = aArgs[0]; // reference back to the AC instance
 
            var elLI = aArgs[1]; // reference to the selected LI element
 
            var oData = aArgs[2]; // object literal of selected item's result data
 

	
 
            //fill the autocomplete with value
 

	
 
            if (oData.nname != undefined) {
 
                addReviewMember(oData.id, oData.fname, oData.lname, oData.nname,
 
                                oData.gravatar_lnk);
 
                myAC.dataSource.cache.push(oData.id);
 
                YUD.get('user').value = ''
 
            }
 
        });
 
    }
 
    return {
 
        ownerDS: ownerDS,
 
        reviewerAC: reviewerAC,
 
    };
 
}
 

	
 
/**
 
 * QUICK REPO MENU
 
 */
 
var quick_repo_menu = function(){
 
    YUE.on(YUQ('.quick_repo_menu'),'mouseenter',function(e){
 
            var menu = e.currentTarget.firstElementChild.firstElementChild;
 
            if(YUD.hasClass(menu,'hidden')){
 
                YUD.replaceClass(e.currentTarget,'hidden', 'active');
 
                YUD.replaceClass(menu, 'hidden', 'active');
 
            }
 
        })
 
    YUE.on(YUQ('.quick_repo_menu'),'mouseleave',function(e){
 
            var menu = e.currentTarget.firstElementChild.firstElementChild;
 
            if(YUD.hasClass(menu,'active')){
 
                YUD.replaceClass(e.currentTarget, 'active', 'hidden');
 
                YUD.replaceClass(menu, 'active', 'hidden');
 
            }
 
        })
 
};
 

	
 

	
 
/**
 
 * TABLE SORTING
 
 */
 

	
 
// returns a node from given html;
 
var fromHTML = function(html){
 
      var _html = document.createElement('element');
 
      _html.innerHTML = html;
 
      return _html;
 
}
 
var get_rev = function(node){
 
    var n = node.firstElementChild.firstElementChild;
 

	
 
    if (n===null){
 
        return -1
 
    }
 
    else{
 
        out = n.firstElementChild.innerHTML.split(':')[0].replace('r','');
 
        return parseInt(out);
 
    }
 
}
 

	
 
var get_name = function(node){
 
     var name = node.firstElementChild.children[2].innerHTML;
 
     return name
 
}
 
var get_group_name = function(node){
 
    var name = node.firstElementChild.children[1].innerHTML;
 
    return name
 
}
 
var get_date = function(node){
 
    var date_ = YUD.getAttribute(node.firstElementChild,'date');
 
    return date_
 
}
 

	
 
var get_age = function(node){
 
    return node
 
}
 

	
 
var get_link = function(node){
 
    return node.firstElementChild.text;
 
}
 

	
 
var revisionSort = function(a, b, desc, field) {
 

	
 
      var a_ = fromHTML(a.getData(field));
 
      var b_ = fromHTML(b.getData(field));
 

	
 
      // extract revisions from string nodes
 
      a_ = get_rev(a_)
 
      b_ = get_rev(b_)
 

	
 
      var comp = YAHOO.util.Sort.compare;
 
      var compState = comp(a_, b_, desc);
 
      return compState;
 
};
 
var ageSort = function(a, b, desc, field) {
 
    var a_ = fromHTML(a.getData(field));
 
    var b_ = fromHTML(b.getData(field));
 

	
 
    // extract name from table
 
    a_ = get_date(a_)
 
    b_ = get_date(b_)
 

	
 
    var comp = YAHOO.util.Sort.compare;
 
    var compState = comp(a_, b_, desc);
 
    return compState;
 
};
 

	
 
var lastLoginSort = function(a, b, desc, field) {
 
    var a_ = a.getData('last_login_raw') || 0;
 
    var b_ = b.getData('last_login_raw') || 0;
 

	
 
    var comp = YAHOO.util.Sort.compare;
 
    var compState = comp(a_, b_, desc);
 
    return compState;
 
};
 

	
 
var nameSort = function(a, b, desc, field) {
 
    var a_ = fromHTML(a.getData(field));
 
    var b_ = fromHTML(b.getData(field));
 

	
 
    // extract name from table
 
    a_ = get_name(a_)
 
    b_ = get_name(b_)
 

	
 
    var comp = YAHOO.util.Sort.compare;
 
    var compState = comp(a_, b_, desc);
 
    return compState;
 
};
 

	
 
var permNameSort = function(a, b, desc, field) {
 
    var a_ = fromHTML(a.getData(field));
 
    var b_ = fromHTML(b.getData(field));
 
    // extract name from table
 

	
 
    a_ = a_.children[0].innerHTML;
 
    b_ = b_.children[0].innerHTML;
 

	
 
    var comp = YAHOO.util.Sort.compare;
 
    var compState = comp(a_, b_, desc);
 
    return compState;
 
};
 

	
 
var groupNameSort = function(a, b, desc, field) {
 
    var a_ = fromHTML(a.getData(field));
 
    var b_ = fromHTML(b.getData(field));
 

	
 
    // extract name from table
 
    a_ = get_group_name(a_)
 
    b_ = get_group_name(b_)
 

	
 
    var comp = YAHOO.util.Sort.compare;
 
    var compState = comp(a_, b_, desc);
 
    return compState;
 
};
 
var dateSort = function(a, b, desc, field) {
 
    var a_ = fromHTML(a.getData(field));
 
    var b_ = fromHTML(b.getData(field));
 

	
 
    // extract name from table
 
    a_ = get_date(a_)
 
    b_ = get_date(b_)
 

	
 
    var comp = YAHOO.util.Sort.compare;
 
    var compState = comp(a_, b_, desc);
 
    return compState;
 
};
 

	
 
var usernamelinkSort = function(a, b, desc, field) {
 
      var a_ = fromHTML(a.getData(field));
 
      var b_ = fromHTML(b.getData(field));
 

	
 
      // extract url text from string nodes
 
      a_ = get_link(a_)
 
      b_ = get_link(b_)
 
      var comp = YAHOO.util.Sort.compare;
 
      var compState = comp(a_, b_, desc);
 
      return compState;
 
}
 

	
 
var addPermAction = function(_html, users_list, groups_list){
 
    var elmts = YUD.getElementsByClassName('last_new_member');
 
    var last_node = elmts[elmts.length-1];
 
    if (last_node){
 
       var next_id = (YUD.getElementsByClassName('new_members')).length;
 
       _html = _html.format(next_id);
 
       last_node.innerHTML = _html;
 
       YUD.setStyle(last_node, 'display', '');
 
       YUD.removeClass(last_node, 'last_new_member');
 
       MembersAutoComplete("perm_new_member_name_"+next_id,
 
               "perm_container_"+next_id, users_list, groups_list);
 
       //create new last NODE
 
       var el = document.createElement('tr');
 
       el.id = 'add_perm_input';
 
       YUD.addClass(el,'last_new_member');
 
       YUD.addClass(el,'new_members');
 
       YUD.insertAfter(el, last_node);
 
    }
 
}
 
function ajaxActionRevokePermission(url, obj_id, obj_type, field_id, extra_data) {
 
    var callback = {
 
        success: function (o) {
 
            var tr = YUD.get(String(field_id));
 
            tr.parentNode.removeChild(tr);
 
        },
 
        failure: function (o) {
 
            alert(_TM['Failed to remoke permission'] + ": " + o.status);
 
        },
 
    };
 
    query_params = {
 
        '_method': 'delete'
 
    }
 
    // put extra data into POST
 
    if (extra_data !== undefined && (typeof extra_data === 'object')){
 
        for(k in extra_data){
 
            query_params[k] = extra_data[k];
 
        }
 
    }
 

	
 
    if (obj_type=='user'){
 
        query_params['user_id'] = obj_id;
 
        query_params['obj_type'] = 'user';
 
    }
 
    else if (obj_type=='user_group'){
 
        query_params['user_group_id'] = obj_id;
 
        query_params['obj_type'] = 'user_group';
 
    }
 

	
 
    var request = YAHOO.util.Connect.asyncRequest('POST', url, callback,
 
            toQueryString(query_params));
 
};
 
/* Multi selectors */
 

	
 
var MultiSelectWidget = function(selected_id, available_id, form_id){
 

	
 

	
 
    //definition of containers ID's
 
    var selected_container = selected_id;
 
    var available_container = available_id;
 

	
 
    //temp container for selected storage.
 
    var cache = new Array();
 
    var av_cache = new Array();
 
    var c =  YUD.get(selected_container);
 
    var ac = YUD.get(available_container);
 

	
 
    //get only selected options for further fullfilment
 
    for(var i = 0;node =c.options[i];i++){
 
        if(node.selected){
 
            //push selected to my temp storage left overs :)
 
            cache.push(node);
 
        }
 
    }
 

	
 
    //get all available options to cache
 
    for(var i = 0;node =ac.options[i];i++){
 
            //push selected to my temp storage left overs :)
 
            av_cache.push(node);
 
    }
 

	
 
    //fill available only with those not in chosen
 
    ac.options.length=0;
 
    tmp_cache = new Array();
 

	
 
    for(var i = 0;node = av_cache[i];i++){
 
        var add = true;
 
        for(var i2 = 0;node_2 = cache[i2];i2++){
 
            if(node.value == node_2.value){
 
                add=false;
 
                break;
 
            }
 
        }
 
        if(add){
 
            tmp_cache.push(new Option(node.text, node.value, false, false));
 
        }
 
    }
 

	
 
    for(var i = 0;node = tmp_cache[i];i++){
 
        ac.options[i] = node;
 
    }
 

	
 
    function prompts_action_callback(e){
 

	
 
        var chosen = YUD.get(selected_container);
 
        var available = YUD.get(available_container);
 

	
 
        //get checked and unchecked options from field
 
        function get_checked(from_field){
 
            //temp container for storage.
 
            var sel_cache = new Array();
 
            var oth_cache = new Array();
 

	
 
            for(var i = 0;node = from_field.options[i];i++){
 
                if(node.selected){
 
                    //push selected fields :)
 
                    sel_cache.push(node);
 
                }
 
                else{
 
                    oth_cache.push(node)
 
                }
 
            }
 

	
 
            return [sel_cache,oth_cache]
 
        }
 

	
 
        //fill the field with given options
 
        function fill_with(field,options){
 
            //clear firtst
 
            field.options.length=0;
 
            for(var i = 0;node = options[i];i++){
 
                    field.options[i]=new Option(node.text, node.value,
 
                            false, false);
 
            }
 

	
 
        }
 
        //adds to current field
 
        function add_to(field,options){
 
            for(var i = 0;node = options[i];i++){
 
                    field.appendChild(new Option(node.text, node.value,
 
                            false, false));
 
            }
 
        }
 

	
 
        // add action
 
        if (this.id=='add_element'){
 
            var c = get_checked(available);
 
            add_to(chosen,c[0]);
 
            fill_with(available,c[1]);
 
        }
 
        // remove action
 
        if (this.id=='remove_element'){
 
            var c = get_checked(chosen);
 
            add_to(available,c[0]);
 
            fill_with(chosen,c[1]);
 
        }
 
        // add all elements
 
        if(this.id=='add_all_elements'){
 
            for(var i=0; node = available.options[i];i++){
 
                    chosen.appendChild(new Option(node.text,
 
                            node.value, false, false));
 
            }
 
            available.options.length = 0;
 
        }
 
        //remove all elements
 
        if(this.id=='remove_all_elements'){
 
            for(var i=0; node = chosen.options[i];i++){
 
                available.appendChild(new Option(node.text,
 
                        node.value, false, false));
 
            }
 
            chosen.options.length = 0;
 
        }
 

	
 
    }
 

	
 
    YUE.addListener(['add_element','remove_element',
 
                   'add_all_elements','remove_all_elements'],'click',
 
                   prompts_action_callback)
 
    if (form_id !== undefined) {
 
        YUE.addListener(form_id,'submit',function(){
 
            var chosen = YUD.get(selected_container);
 
            for (var i = 0; i < chosen.options.length; i++) {
 
                chosen.options[i].selected = 'selected';
 
            }
 
        });
 
    }
 
}
 

	
 

	
 
// global hooks after DOM is loaded
 

	
 
YUE.onDOMReady(function(){
 
    YUE.on(YUQ('.diff-collapse-button'), 'click', function(e){
 
        var button = e.currentTarget;
 
        var t = YUD.get(button).getAttribute('target');
 
        console.log(t);
 
        if(YUD.hasClass(t, 'hidden')){
 
            YUD.removeClass(t, 'hidden');
 
            YUD.get(button).innerHTML = "&uarr; {0} &uarr;".format(_TM['Collapse diff']);
 
        }
 
        else if(!YUD.hasClass(t, 'hidden')){
 
            YUD.addClass(t, 'hidden');
 
            YUD.get(button).innerHTML = "&darr; {0} &darr;".format(_TM['Expand diff']);
 
        }
 
    });
 

	
 

	
 

	
 
});
rhodecode/templates/admin/repos/repo_edit_perms.html
Show inline comments
 
<table id="permissions_manage" class="noborder">
 
    <tr>
 
        <td>${_('none')}</td>
 
        <td>${_('read')}</td>
 
        <td>${_('write')}</td>
 
        <td>${_('admin')}</td>
 
        <td>${_('member')}</td>
 
        <td></td>
 
    </tr>
 
    ## USERS
 
    %for r2p in c.repo_info.repo_to_perm:
 
        %if r2p.user.username =='default' and c.repo_info.private:
 
            <tr>
 
                <td colspan="4">
 
                    <span class="private_repo_msg">
 
                    ${_('private repository')}
 
                    </span>
 
                </td>
 
                <td class="private_repo_msg"><img style="vertical-align:bottom" src="${h.url('/images/icons/user.png')}"/>${_('default')}</td>
 
            </tr>
 
        %else:
 
        <tr id="id${id(r2p.user.username)}">
 
            <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.none')}</td>
 
            <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.read')}</td>
 
            <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.write')}</td>
 
            <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.admin')}</td>
 
            <td style="white-space: nowrap;">
 
                <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username if r2p.user.username != 'default' else _('default')}
 
            </td>
 
            <td>
 
              %if r2p.user.username !='default':
 
                <span class="delete_icon action_button" onclick="ajaxActionRevoke(${r2p.user.user_id}, 'user', '${'id%s'%id(r2p.user.username)}')">
 
                ${_('revoke')}
 
                </span>
 
              %endif
 
            </td>
 
        </tr>
 
        %endif
 
    %endfor
 

	
 
    ## USER GROUPS
 
    %for g2p in c.repo_info.users_group_to_perm:
 
        <tr id="id${id(g2p.users_group.users_group_name)}">
 
            <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.none')}</td>
 
            <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.read')}</td>
 
            <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.write')}</td>
 
            <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.admin')}</td>
 
            <td style="white-space: nowrap;">
 
                <img class="perm-gravatar" src="${h.url('/images/icons/group.png')}"/>
 
                %if h.HasPermissionAny('hg.admin')():
 
                 <a href="${h.url('edit_users_group',id=g2p.users_group.users_group_id)}">${g2p.users_group.users_group_name}</a>
 
                %else:
 
                 ${g2p.users_group.users_group_name}
 
                %endif
 
            </td>
 
            <td>
 
                <span class="delete_icon action_button" onclick="ajaxActionRevoke(${g2p.users_group.users_group_id}, 'user_group', '${'id%s'%id(g2p.users_group.users_group_name)}')">
 
                ${_('revoke')}
 
                </span>
 
            </td>
 
        </tr>
 
    %endfor
 
    <%
 
    _tmpl = h.literal("""' \
 
        <td><input type="radio" value="repository.none" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
 
        <td><input type="radio" value="repository.read" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
 
        <td><input type="radio" value="repository.write" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
 
        <td><input type="radio" value="repository.admin" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
 
        <td class="ac"> \
 
            <div class="perm_ac" id="perm_ac_{0}"> \
 
                <input class="yui-ac-input" id="perm_new_member_name_{0}" name="perm_new_member_name_{0}" value="" type="text"> \
 
                <input id="perm_new_member_type_{0}" name="perm_new_member_type_{0}" value="" type="hidden">  \
 
                <div id="perm_container_{0}"></div> \
 
            </div> \
 
        </td> \
 
        <td></td>'""")
 
    %>
 
    ## ADD HERE DYNAMICALLY NEW INPUTS FROM THE '_tmpl'
 
    <tr class="new_members last_new_member" id="add_perm_input"></tr>
 
    <tr>
 
        <td colspan="6">
 
            <span id="add_perm" class="add_icon" style="cursor: pointer;">
 
            ${_('Add another member')}
 
            </span>
 
        </td>
 
    </tr>
 
</table>
 
<script type="text/javascript">
 
function ajaxActionRevoke(obj_id, obj_type, field_id) {
 
    var callback = {
 
        success: function (o) {
 
            var tr = YUD.get(String(field_id));
 
            tr.parentNode.removeChild(tr);
 
        },
 
        failure: function (o) {
 
            alert(_TM['Failed to remoke permission'] + ": " + o.status);
 
        },
 
    url = "${h.url('delete_repo_perm_member',repo_name=c.repo_name)}";
 
    ajaxActionRevokePermission(url, obj_id, obj_type, field_id);
 
    };
 
    if (obj_type=='user'){
 
        var sUrl = "${h.url('delete_repo_user',repo_name=c.repo_name)}";
 
        var postData = '_method=delete&user_id={0}&obj_type=user'.format(obj_id);
 
    }
 
    else if (obj_type=='user_group'){
 
        var sUrl = "${h.url('delete_repo_users_group',repo_name=c.repo_name)}";
 
        var postData = '_method=delete&users_group_id={0}&obj_type=user_group'.format(obj_id);
 

	
 
    }
 

	
 
    var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
 
};
 

	
 

	
 
YUE.onDOMReady(function () {
 
    if (!YUD.hasClass('perm_new_member_name', 'error')) {
 
        YUD.setStyle('add_perm_input', 'display', 'none');
 
    }
 
    YAHOO.util.Event.addListener('add_perm', 'click', function () {
 
        addPermAction(${_tmpl}, ${c.users_array|n}, ${c.users_groups_array|n});
 
    });
 
});
 

	
 
</script>
rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html
Show inline comments
 
<table id="permissions_manage" class="noborder">
 
    <tr>
 
        <td>${_('none')}</td>
 
        <td>${_('read')}</td>
 
        <td>${_('write')}</td>
 
        <td>${_('admin')}</td>
 
        <td>${_('member')}</td>
 
        <td></td>
 
    </tr>
 
    ## USERS
 
    %for r2p in c.repos_group.repo_group_to_perm:
 
        ##forbid revoking permission from yourself
 
        <tr id="id${id(r2p.user.username)}">
 
            %if c.rhodecode_user.user_id != r2p.user.user_id or c.rhodecode_user.is_admin:
 
            <td>${h.radio('u_perm_%s' % r2p.user.username,'group.none')}</td>
 
            <td>${h.radio('u_perm_%s' % r2p.user.username,'group.read')}</td>
 
            <td>${h.radio('u_perm_%s' % r2p.user.username,'group.write')}</td>
 
            <td>${h.radio('u_perm_%s' % r2p.user.username,'group.admin')}</td>
 
            <td style="white-space: nowrap;">
 
                <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username if r2p.user.username != 'default' else _('default')}
 
            </td>
 
            <td>
 
              %if r2p.user.username !='default':
 
                <span class="delete_icon action_button" onclick="ajaxActionRevoke(${r2p.user.user_id}, 'user', '${'id%s'%id(r2p.user.username)}')">
 
                ${_('revoke')}
 
                </span>
 
              %endif
 
            </td>
 
            %else:
 
            <td>${h.radio('u_perm_%s' % r2p.user.username,'group.none', disabled="disabled")}</td>
 
            <td>${h.radio('u_perm_%s' % r2p.user.username,'group.read', disabled="disabled")}</td>
 
            <td>${h.radio('u_perm_%s' % r2p.user.username,'group.write', disabled="disabled")}</td>
 
            <td>${h.radio('u_perm_%s' % r2p.user.username,'group.admin', disabled="disabled")}</td>
 
            <td style="white-space: nowrap;">
 
                <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username if r2p.user.username != 'default' else _('default')}
 
            </td>
 
            <td>
 
            </td>
 
            %endif
 
        </tr>
 
    %endfor
 

	
 
    ## USER GROUPS
 
    %for g2p in c.repos_group.users_group_to_perm:
 
        <tr id="id${id(g2p.users_group.users_group_name)}">
 
            <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.none')}</td>
 
            <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.read')}</td>
 
            <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.write')}</td>
 
            <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.admin')}</td>
 
            <td style="white-space: nowrap;">
 
                <img class="perm-gravatar" src="${h.url('/images/icons/group.png')}"/>${g2p.users_group.users_group_name}
 
            </td>
 
            <td>
 
                <span class="delete_icon action_button" onclick="ajaxActionRevoke(${g2p.users_group.users_group_id}, 'user_group', '${'id%s'%id(g2p.users_group.users_group_name)}')">
 
                ${_('revoke')}
 
                </span>
 
            </td>
 
        </tr>
 
    %endfor
 
<%
 
    _tmpl = h.literal("""' \
 
        <td><input type="radio" value="group.none" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
 
        <td><input type="radio" value="group.read" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
 
        <td><input type="radio" value="group.write" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
 
        <td><input type="radio" value="group.admin" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
 
        <td class="ac"> \
 
            <div class="perm_ac" id="perm_ac_{0}"> \
 
                <input class="yui-ac-input" id="perm_new_member_name_{0}" name="perm_new_member_name_{0}" value="" type="text"> \
 
                <input id="perm_new_member_type_{0}" name="perm_new_member_type_{0}" value="" type="hidden">  \
 
                <div id="perm_container_{0}"></div> \
 
            </div> \
 
        </td> \
 
        <td></td>'""")
 
    %>
 
    ## ADD HERE DYNAMICALLY NEW INPUTS FROM THE '_tmpl'
 
    <tr class="new_members last_new_member" id="add_perm_input"></tr>
 
    <tr>
 
        <td colspan="6">
 
            <span id="add_perm" class="add_icon" style="cursor: pointer;">
 
            ${_('Add another member')}
 
            </span>
 
        </td>
 
    </tr>
 
    <tr>
 
        <td colspan="6">
 
           ${h.checkbox('recursive',value="True", label=_('apply to children'))}
 
           <span class="help-block">${_('Set or revoke permission to all children of that group, including non-private repositories and other groups')}</span>
 
        </td>
 
    </tr>
 
</table>
 
<script type="text/javascript">
 
function ajaxActionRevoke(obj_id, obj_type, field_id) {
 
    var callback = {
 
        success: function (o) {
 
            var tr = YUD.get(String(field_id));
 
            tr.parentNode.removeChild(tr);
 
        },
 
        failure: function (o) {
 
            alert(_TM['Failed to remoke permission'] + ": " + o.status);
 
        },
 
    url = "${h.url('delete_repo_group_perm_member', group_name=c.repos_group.group_name)}";
 
    ajaxActionRevokePermission(url, obj_id, obj_type, field_id, {recursive:YUD.get('recursive').checked});
 
    };
 
    var recursive = YUD.get('recursive').checked;
 

	
 
    if (obj_type=='user'){
 
        var sUrl = "${h.url('delete_repos_group_user_perm',group_name=c.repos_group.group_name)}";
 
        var postData = '_method=delete&recursive={0}&user_id={1}&obj_type=user'.format(recursive,obj_id);
 
    }
 
    else if (obj_type=='user_group'){
 
        var sUrl = "${h.url('delete_repos_group_users_group_perm',group_name=c.repos_group.group_name)}";
 
        var postData = '_method=delete&recursive={0}&users_group_id={0}&obj_type=user_group'.format(recursive,obj_id);
 
    }
 
    var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
 
};
 

	
 

	
 
YUE.onDOMReady(function () {
 
    if (!YUD.hasClass('perm_new_member_name', 'error')) {
 
        YUD.setStyle('add_perm_input', 'display', 'none');
 
    }
 
    YAHOO.util.Event.addListener('add_perm', 'click', function () {
 
        addPermAction(${_tmpl}, ${c.users_array|n}, ${c.users_groups_array|n});
 
    });
 
});
 

	
 
</script>
rhodecode/templates/admin/users_groups/user_group_edit_perms.html
Show inline comments
 
<table id="permissions_manage" class="noborder">
 
    <tr>
 
        <td>${_('none')}</td>
 
        <td>${_('read')}</td>
 
        <td>${_('write')}</td>
 
        <td>${_('admin')}</td>
 
        <td>${_('member')}</td>
 
        <td></td>
 
    </tr>
 
    ## USERS
 
    %for r2p in c.users_group.user_user_group_to_perm:
 
        ##forbid revoking permission from yourself
 
        <tr id="id${id(r2p.user.username)}">
 
            %if c.rhodecode_user.user_id != r2p.user.user_id or c.rhodecode_user.is_admin:
 
            <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.none')}</td>
 
            <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.read')}</td>
 
            <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.write')}</td>
 
            <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.admin')}</td>
 
            <td style="white-space: nowrap;">
 
                <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username if r2p.user.username != 'default' else _('default')}
 
            </td>
 
            <td>
 
              %if r2p.user.username !='default':
 
                <span class="delete_icon action_button" onclick="ajaxActionRevoke(${r2p.user.user_id}, 'user', '${'id%s'%id(r2p.user.username)}')">
 
                ${_('revoke')}
 
                </span>
 
              %endif
 
            </td>
 
            %else:
 
            <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.none', disabled="disabled")}</td>
 
            <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.read', disabled="disabled")}</td>
 
            <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.write', disabled="disabled")}</td>
 
            <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.admin', disabled="disabled")}</td>
 
            <td style="white-space: nowrap;">
 
                <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username if r2p.user.username != 'default' else _('default')}
 
            </td>
 
            <td>
 
            </td>
 
            %endif
 
        </tr>
 
    %endfor
 

	
 
    <%
 
    _tmpl = h.literal("""' \
 
        <td><input type="radio" value="usergroup.none" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
 
        <td><input type="radio" value="usergroup.read" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
 
        <td><input type="radio" value="usergroup.write" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
 
        <td><input type="radio" value="usergroup.admin" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
 
        <td class="ac"> \
 
            <div class="perm_ac" id="perm_ac_{0}"> \
 
                <input class="yui-ac-input" id="perm_new_member_name_{0}" name="perm_new_member_name_{0}" value="" type="text"> \
 
                <input id="perm_new_member_type_{0}" name="perm_new_member_type_{0}" value="" type="hidden">  \
 
                <div id="perm_container_{0}"></div> \
 
            </div> \
 
        </td> \
 
        <td></td>'""")
 
    %>
 
    ## ADD HERE DYNAMICALLY NEW INPUTS FROM THE '_tmpl'
 
    <tr class="new_members last_new_member" id="add_perm_input"></tr>
 
    <tr>
 
        <td colspan="6">
 
            <span id="add_perm" class="add_icon" style="cursor: pointer;">
 
            ${_('Add another member')}
 
            </span>
 
        </td>
 
    </tr>
 
</table>
 
<script type="text/javascript">
 
function ajaxActionRevoke(obj_id, obj_type, field_id) {
 
    var callback = {
 
        success: function (o) {
 
            var tr = YUD.get(String(field_id));
 
            tr.parentNode.removeChild(tr);
 
        },
 
        failure: function (o) {
 
            alert(_TM['Failed to remoke permission'] + ": " + o.status);
 
        },
 
    url = "${h.url('delete_user_group_perm_member', id=c.users_group.users_group_id)}";
 
    ajaxActionRevokePermission(url, obj_id, obj_type, field_id);
 
    };
 
    var sUrl = "${h.url('delete_user_group_perm_member', id=c.users_group.users_group_id)}";
 
    if (obj_type=='user'){
 
        var postData = '_method=delete&user_id={0}&obj_type=user'.format(obj_id);
 
    }
 
    else if (obj_type=='user_group'){
 
        var postData = '_method=delete&user_group_id={0}&obj_type=user_group'.format(obj_id);
 
    }
 

	
 
    var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
 
};
 

	
 

	
 
YUE.onDOMReady(function () {
 
    if (!YUD.hasClass('perm_new_member_name', 'error')) {
 
        YUD.setStyle('add_perm_input', 'display', 'none');
 
    }
 
    YAHOO.util.Event.addListener('add_perm', 'click', function () {
 
        addPermAction(${_tmpl}, ${c.users_array|n}, ${c.users_groups_array|n});
 
    });
 
});
 

	
 
</script>
0 comments (0 inline, 0 general)