Changeset - b8830c373681
[Not reviewed]
default
0 7 0
Daniel Hobley - 10 years ago 2015-05-21 16:48:13
danielh@unity3d.com
datatables: use jQuery DataTables instead of base.js YUI_datatable wrapper of YAHOO.widget.DataTable

Sorting and searching fixes by Mads Kiilerich.
7 files changed with 105 insertions and 276 deletions:
0 comments (0 inline, 0 general)
kallithea/model/repo.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
# 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/>.
 
"""
 
kallithea.model.repo
 
~~~~~~~~~~~~~~~~~~~~
 

	
 
Repository model for kallithea
 

	
 
This file was forked by the Kallithea project in July 2014.
 
Original author and date, and relevant copyright and licensing information is below:
 
:created_on: Jun 5, 2010
 
:author: marcink
 
:copyright: (c) 2013 RhodeCode GmbH, and others.
 
:license: GPLv3, see LICENSE.md for more details.
 

	
 
"""
 

	
 
import os
 
import shutil
 
import logging
 
import traceback
 
from datetime import datetime
 
from sqlalchemy.orm import subqueryload
 

	
 
from kallithea.lib.utils import make_ui
 
from kallithea.lib.vcs.backends import get_backend
 
from kallithea.lib.compat import json
 
from kallithea.lib.utils2 import LazyProperty, safe_str, safe_unicode, \
 
    remove_prefix, obfuscate_url_pw, get_current_authuser
 
from kallithea.lib.caching_query import FromCache
 
from kallithea.lib.hooks import log_delete_repository
 

	
 
from kallithea.model import BaseModel
 
from kallithea.model.db import Repository, UserRepoToPerm, UserGroupRepoToPerm, \
 
    UserRepoGroupToPerm, UserGroupRepoGroupToPerm, User, Permission, \
 
    Statistics, UserGroup, Ui, RepoGroup, RepositoryField
 

	
 
from kallithea.lib import helpers as h
 
from kallithea.lib.auth import HasRepoPermissionAny, HasUserGroupPermissionAny
 
from kallithea.lib.exceptions import AttachedForksError
 
from kallithea.model.scm import UserGroupList
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class RepoModel(BaseModel):
 

	
 
    cls = Repository
 
    URL_SEPARATOR = Repository.url_sep()
 

	
 
    def _get_user_group(self, users_group):
 
        return self._get_instance(UserGroup, users_group,
 
                                  callback=UserGroup.get_by_group_name)
 

	
 
    def _get_repo_group(self, repo_group):
 
        return self._get_instance(RepoGroup, repo_group,
 
                                  callback=RepoGroup.get_by_group_name)
 

	
 
    def _create_default_perms(self, repository, private):
 
        # create default permission
 
        default = 'repository.read'
 
        def_user = User.get_default_user()
 
        for p in def_user.user_perms:
 
            if p.permission.permission_name.startswith('repository.'):
 
                default = p.permission.permission_name
 
                break
 

	
 
        default_perm = 'repository.none' if private else default
 

	
 
        repo_to_perm = UserRepoToPerm()
 
        repo_to_perm.permission = Permission.get_by_key(default_perm)
 

	
 
        repo_to_perm.repository = repository
 
        repo_to_perm.user_id = def_user.user_id
 

	
 
        return repo_to_perm
 

	
 
    @LazyProperty
 
    def repos_path(self):
 
        """
 
        Gets the repositories root path from database
 
        """
 

	
 
        q = self.sa.query(Ui).filter(Ui.ui_key == '/').one()
 
        return q.ui_value
 

	
 
    def get(self, repo_id, cache=False):
 
        repo = self.sa.query(Repository) \
 
            .filter(Repository.repo_id == repo_id)
 

	
 
        if cache:
 
            repo = repo.options(FromCache("sql_cache_short",
 
                                          "get_repo_%s" % repo_id))
 
        return repo.scalar()
 

	
 
    def get_repo(self, repository):
 
        return self._get_repo(repository)
 

	
 
    def get_by_repo_name(self, repo_name, cache=False):
 
        repo = self.sa.query(Repository) \
 
            .filter(Repository.repo_name == repo_name)
 

	
 
        if cache:
 
            repo = repo.options(FromCache("sql_cache_short",
 
                                          "get_repo_%s" % repo_name))
 
        return repo.scalar()
 

	
 
    def get_all_user_repos(self, user):
 
        """
 
        Gets all repositories that user have at least read access
 

	
 
        :param user:
 
        """
 
        from kallithea.lib.auth import AuthUser
 
        user = self._get_user(user)
 
        repos = AuthUser(user_id=user.user_id).permissions['repositories']
 
        access_check = lambda r: r[1] in ['repository.read',
 
                                          'repository.write',
 
                                          'repository.admin']
 
        repos = [x[0] for x in filter(access_check, repos.items())]
 
        return Repository.query().filter(Repository.repo_name.in_(repos))
 

	
 
    def get_users_js(self):
 
        users = self.sa.query(User) \
 
            .filter(User.active == True) \
 
            .order_by(User.name, User.lastname) \
 
            .all()
 
        return json.dumps([
 
            {
 
                'id': u.user_id,
 
                'fname': h.escape(u.name),
 
                'lname': h.escape(u.lastname),
 
                'nname': u.username,
 
                'gravatar_lnk': h.gravatar_url(u.email, size=28, default='default'),
 
                'gravatar_size': 14,
 
            } for u in users]
 
        )
 

	
 
    def get_user_groups_js(self):
 
        user_groups = self.sa.query(UserGroup) \
 
            .filter(UserGroup.users_group_active == True) \
 
            .order_by(UserGroup.users_group_name) \
 
            .options(subqueryload(UserGroup.members)) \
 
            .all()
 
        user_groups = UserGroupList(user_groups, perm_set=['usergroup.read',
 
                                                           'usergroup.write',
 
                                                           'usergroup.admin'])
 
        return json.dumps([
 
            {
 
                'id': gr.users_group_id,
 
                'grname': gr.users_group_name,
 
                'grmembers': len(gr.members),
 
            } for gr in user_groups]
 
        )
 

	
 
    @classmethod
 
    def _render_datatable(cls, tmpl, *args, **kwargs):
 
        import kallithea
 
        from pylons import tmpl_context as c
 
        from pylons.i18n.translation import _
 

	
 
        _tmpl_lookup = kallithea.CONFIG['pylons.app_globals'].mako_lookup
 
        template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
 

	
 
        tmpl = template.get_def(tmpl)
 
        kwargs.update(dict(_=_, h=h, c=c))
 
        return tmpl.render(*args, **kwargs)
 

	
 
    @classmethod
 
    def update_repoinfo(cls, repositories=None):
 
        if repositories is None:
 
            repositories = Repository.getAll()
 
        for repo in repositories:
 
            repo.update_changeset_cache()
 

	
 
    def get_repos_as_dict(self, repos_list=None, admin=False, perm_check=True,
 
                          super_user_actions=False, short_name=False):
 
        _render = self._render_datatable
 
        from pylons import tmpl_context as c
 

	
 
        def quick_menu(repo_name):
 
            return _render('quick_menu', repo_name)
 

	
 
        def repo_lnk(name, rtype, rstate, private, fork_of):
 
            return _render('repo_name', name, rtype, rstate, private, fork_of,
 
                           short_name=short_name, admin=False)
 

	
 
        def last_change(last_change):
 
            return _render("last_change", last_change)
 

	
 
        def rss_lnk(repo_name):
 
            return _render("rss", repo_name)
 

	
 
        def atom_lnk(repo_name):
 
            return _render("atom", repo_name)
 

	
 
        def last_rev(repo_name, cs_cache):
 
            return _render('revision', repo_name, cs_cache.get('revision'),
 
                           cs_cache.get('raw_id'), cs_cache.get('author'),
 
                           cs_cache.get('message'))
 

	
 
        def desc(desc):
 
            return h.urlify_text(desc, truncate=80, stylize=c.visual.stylify_metatags)
 

	
 
        def state(repo_state):
 
            return _render("repo_state", repo_state)
 

	
 
        def repo_actions(repo_name):
 
            return _render('repo_actions', repo_name, super_user_actions)
 

	
 
        def owner_actions(user_id, username):
 
            return _render('user_name', user_id, username)
 

	
 
        repos_data = []
 
        for repo in repos_list:
 
            if perm_check:
 
                # check permission at this level
 
                if not HasRepoPermissionAny(
 
                        'repository.read', 'repository.write',
 
                        'repository.admin'
 
                )(repo.repo_name, 'get_repos_as_dict check'):
 
                    continue
 
            cs_cache = repo.changeset_cache
 
            row = {
 
                "menu": quick_menu(repo.repo_name),
 
                "raw_name": repo.repo_name,
 
                "name": repo_lnk(repo.repo_name, repo.repo_type,
 
                                 repo.repo_state, repo.private, repo.fork),
 
                "last_change_iso": repo.last_db_change.isoformat(),
 
                "last_change": last_change(repo.last_db_change),
 
                "last_changeset": last_rev(repo.repo_name, cs_cache),
 
                "last_rev_raw": cs_cache.get('revision'),
 
                "desc": desc(repo.description),
 
                "owner": h.person(repo.user),
 
                "state": state(repo.repo_state),
 
                "rss": rss_lnk(repo.repo_name),
 
                "atom": atom_lnk(repo.repo_name),
 

	
 
            }
 
            if admin:
 
                row.update({
 
                    "action": repo_actions(repo.repo_name),
 
                    "owner": owner_actions(repo.user.user_id,
 
                                           h.person(repo.user))
 
                })
 
            repos_data.append(row)
 

	
 
        return {
 
            "totalRecords": len(repos_list),
 
            "startIndex": 0,
 
            "sort": "name",
 
            "dir": "asc",
 
            "records": repos_data
 
        }
 

	
 
    def _get_defaults(self, repo_name):
 
        """
 
        Gets information about repository, and returns a dict for
 
        usage in forms
 

	
 
        :param repo_name:
 
        """
 

	
 
        repo_info = Repository.get_by_repo_name(repo_name)
 

	
 
        if repo_info is None:
 
            return None
 

	
 
        defaults = repo_info.get_dict()
 
        group, repo_name, repo_name_full = repo_info.groups_and_repo
 
        defaults['repo_name'] = repo_name
 
        defaults['repo_group'] = getattr(group[-1] if group else None,
 
                                         'group_id', None)
 

	
 
        for strip, k in [(0, 'repo_type'), (1, 'repo_enable_downloads'),
 
                         (1, 'repo_description'), (1, 'repo_enable_locking'),
 
                         (1, 'repo_landing_rev'), (0, 'clone_uri'),
 
                         (1, 'repo_private'), (1, 'repo_enable_statistics')]:
 
            attr = k
 
            if strip:
 
                attr = remove_prefix(k, 'repo_')
 

	
 
            val = defaults[attr]
 
            if k == 'repo_landing_rev':
 
                val = ':'.join(defaults[attr])
 
            defaults[k] = val
 
            if k == 'clone_uri':
 
                defaults['clone_uri_hidden'] = repo_info.clone_uri_hidden
 

	
 
        # fill owner
 
        if repo_info.user:
 
            defaults.update({'user': repo_info.user.username})
 
        else:
 
            replacement_user = User.query().filter(User.admin ==
 
                                                   True).first().username
 
            defaults.update({'user': replacement_user})
 

	
 
        # fill repository users
 
        for p in repo_info.repo_to_perm:
 
            defaults.update({'u_perm_%s' % p.user.username:
 
                                 p.permission.permission_name})
 

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

	
 
        return defaults
 

	
 
    def update(self, repo, **kwargs):
 
        try:
 
            cur_repo = self._get_repo(repo)
 
            org_repo_name = cur_repo.repo_name
 
            if 'user' in kwargs:
 
                cur_repo.user = User.get_by_username(kwargs['user'])
 

	
 
            if 'repo_group' in kwargs:
 
                cur_repo.group = RepoGroup.get(kwargs['repo_group'])
 
            log.debug('Updating repo %s with params:%s', cur_repo, kwargs)
 
            for k in ['repo_enable_downloads',
 
                      'repo_description',
 
                      'repo_enable_locking',
 
                      'repo_landing_rev',
 
                      'repo_private',
 
                      'repo_enable_statistics',
 
                      ]:
 
                if k in kwargs:
 
                    setattr(cur_repo, remove_prefix(k, 'repo_'), kwargs[k])
 
            clone_uri = kwargs.get('clone_uri')
 
            if clone_uri is not None and clone_uri != cur_repo.clone_uri_hidden:
 
                cur_repo.clone_uri = clone_uri
 

	
 
            new_name = cur_repo.get_new_name(kwargs['repo_name'])
 
            cur_repo.repo_name = new_name
 
            #if private flag is set, reset default permission to NONE
 

	
 
            if kwargs.get('repo_private'):
 
                EMPTY_PERM = 'repository.none'
 
                RepoModel().grant_user_permission(
 
                    repo=cur_repo, user='default', perm=EMPTY_PERM
 
                )
 
                #handle extra fields
 
            for field in filter(lambda k: k.startswith(RepositoryField.PREFIX),
 
                                kwargs):
 
                k = RepositoryField.un_prefix_key(field)
 
                ex_field = RepositoryField.get_by_key_name(key=k, repo=cur_repo)
 
                if ex_field:
 
                    ex_field.field_value = kwargs[field]
 
                    self.sa.add(ex_field)
 
            self.sa.add(cur_repo)
 

	
 
            if org_repo_name != new_name:
 
                # rename repository
 
                self._rename_filesystem_repo(old=org_repo_name, new=new_name)
 

	
 
            return cur_repo
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            raise
 

	
 
    def _create_repo(self, repo_name, repo_type, description, owner,
 
                     private=False, clone_uri=None, repo_group=None,
 
                     landing_rev='rev:tip', fork_of=None,
 
                     copy_fork_permissions=False, enable_statistics=False,
 
                     enable_locking=False, enable_downloads=False,
 
                     copy_group_permissions=False, state=Repository.STATE_PENDING):
 
        """
 
        Create repository inside database with PENDING state. This should only be
 
        executed by create() repo, with exception of importing existing repos.
 

	
 
        """
 
        from kallithea.model.scm import ScmModel
 

	
 
        owner = self._get_user(owner)
 
        fork_of = self._get_repo(fork_of)
 
        repo_group = self._get_repo_group(repo_group)
 
        try:
 
            repo_name = safe_unicode(repo_name)
 
            description = safe_unicode(description)
 
            # repo name is just a name of repository
 
            # while repo_name_full is a full qualified name that is combined
 
            # with name and path of group
 
            repo_name_full = repo_name
 
            repo_name = repo_name.split(self.URL_SEPARATOR)[-1]
 

	
 
            new_repo = Repository()
 
            new_repo.repo_state = state
 
            new_repo.enable_statistics = False
 
            new_repo.repo_name = repo_name_full
 
            new_repo.repo_type = repo_type
 
            new_repo.user = owner
 
            new_repo.group = repo_group
 
            new_repo.description = description or repo_name
 
            new_repo.private = private
 
            new_repo.clone_uri = clone_uri
 
            new_repo.landing_rev = landing_rev
 

	
 
            new_repo.enable_statistics = enable_statistics
 
            new_repo.enable_locking = enable_locking
 
            new_repo.enable_downloads = enable_downloads
 

	
 
            if repo_group:
 
                new_repo.enable_locking = repo_group.enable_locking
 

	
 
            if fork_of:
 
                parent_repo = fork_of
 
                new_repo.fork = parent_repo
 

	
 
            self.sa.add(new_repo)
 

	
 
            if fork_of and copy_fork_permissions:
 
                repo = fork_of
 
                user_perms = UserRepoToPerm.query() \
 
                    .filter(UserRepoToPerm.repository == repo).all()
 
                group_perms = UserGroupRepoToPerm.query() \
 
                    .filter(UserGroupRepoToPerm.repository == repo).all()
 

	
 
                for perm in user_perms:
 
                    UserRepoToPerm.create(perm.user, new_repo, perm.permission)
 

	
 
                for perm in group_perms:
 
                    UserGroupRepoToPerm.create(perm.users_group, new_repo,
 
                                               perm.permission)
 

	
 
            elif repo_group and copy_group_permissions:
 

	
 
                user_perms = UserRepoGroupToPerm.query() \
 
                    .filter(UserRepoGroupToPerm.group == repo_group).all()
 

	
 
                group_perms = UserGroupRepoGroupToPerm.query() \
 
                    .filter(UserGroupRepoGroupToPerm.group == repo_group).all()
 

	
 
                for perm in user_perms:
 
                    perm_name = perm.permission.permission_name.replace('group.', 'repository.')
 
                    perm_obj = Permission.get_by_key(perm_name)
 
                    UserRepoToPerm.create(perm.user, new_repo, perm_obj)
 

	
 
                for perm in group_perms:
 
                    perm_name = perm.permission.permission_name.replace('group.', 'repository.')
 
                    perm_obj = Permission.get_by_key(perm_name)
 
                    UserGroupRepoToPerm.create(perm.users_group, new_repo, perm_obj)
 

	
 
            else:
 
                perm_obj = self._create_default_perms(new_repo, private)
 
                self.sa.add(perm_obj)
 

	
 
            # now automatically start following this repository as owner
 
            ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
 
                                                    owner.user_id)
 
            # we need to flush here, in order to check if database won't
 
            # throw any exceptions, create filesystem dirs at the very end
 
            self.sa.flush()
 
            return new_repo
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            raise
 

	
 
    def create(self, form_data, cur_user):
 
        """
 
        Create repository using celery tasks
 

	
 
        :param form_data:
 
        :param cur_user:
 
        """
 
        from kallithea.lib.celerylib import tasks, run_task
 
        return run_task(tasks.create_repo, form_data, cur_user)
 

	
 
    def _update_permissions(self, repo, perms_new=None, perms_updates=None,
 
                            check_perms=True):
 
        if not perms_new:
 
            perms_new = []
 
        if not perms_updates:
 
            perms_updates = []
 

	
 
        # update permissions
 
        for member, perm, member_type in perms_updates:
 
            if member_type == 'user':
 
                # this updates existing one
 
                self.grant_user_permission(
 
                    repo=repo, user=member, perm=perm
 
                )
 
            else:
 
                #check if we have permissions to alter this usergroup
 
                req_perms = (
 
                    'usergroup.read', 'usergroup.write', 'usergroup.admin')
 
                if not check_perms or HasUserGroupPermissionAny(*req_perms)(
 
                        member):
 
                    self.grant_user_group_permission(
 
                        repo=repo, group_name=member, perm=perm
 
                    )
 
            # set new permissions
 
        for member, perm, member_type in perms_new:
 
            if member_type == 'user':
 
                self.grant_user_permission(
 
                    repo=repo, user=member, perm=perm
 
                )
 
            else:
 
                #check if we have permissions to alter this usergroup
 
                req_perms = (
 
                    'usergroup.read', 'usergroup.write', 'usergroup.admin')
 
                if not check_perms or HasUserGroupPermissionAny(*req_perms)(
 
                        member):
 
                    self.grant_user_group_permission(
 
                        repo=repo, group_name=member, perm=perm
 
                    )
 

	
 
    def create_fork(self, form_data, cur_user):
 
        """
 
        Simple wrapper into executing celery task for fork creation
 

	
 
        :param form_data:
 
        :param cur_user:
 
        """
 
        from kallithea.lib.celerylib import tasks, run_task
 
        return run_task(tasks.create_repo_fork, form_data, cur_user)
 

	
 
    def delete(self, repo, forks=None, fs_remove=True, cur_user=None):
 
        """
 
        Delete given repository, forks parameter defines what do do with
 
        attached forks. Throws AttachedForksError if deleted repo has attached
 
        forks
 

	
 
        :param repo:
 
        :param forks: str 'delete' or 'detach'
 
        :param fs_remove: remove(archive) repo from filesystem
 
        """
 
        if not cur_user:
 
            cur_user = getattr(get_current_authuser(), 'username', None)
 
        repo = self._get_repo(repo)
 
        if repo is not None:
 
            if forks == 'detach':
 
                for r in repo.forks:
 
                    r.fork = None
 
                    self.sa.add(r)
 
            elif forks == 'delete':
 
                for r in repo.forks:
 
                    self.delete(r, forks='delete')
 
            elif [f for f in repo.forks]:
 
                raise AttachedForksError()
 

	
 
            old_repo_dict = repo.get_dict()
 
            try:
 
                self.sa.delete(repo)
 
                if fs_remove:
 
                    self._delete_filesystem_repo(repo)
 
                else:
 
                    log.debug('skipping removal from filesystem')
 
                log_delete_repository(old_repo_dict,
 
                                      deleted_by=cur_user)
 
            except Exception:
 
                log.error(traceback.format_exc())
 
                raise
 

	
 
    def grant_user_permission(self, repo, user, perm):
 
        """
 
        Grant permission for user on given repository, or update existing one
 
        if found
 

	
 
        :param repo: Instance of Repository, repository_id, or repository name
 
        :param user: Instance of User, user_id or username
 
        :param perm: Instance of Permission, or permission_name
 
        """
 
        user = self._get_user(user)
 
        repo = self._get_repo(repo)
 
        permission = self._get_perm(perm)
 

	
 
        # check if we have that permission already
 
        obj = self.sa.query(UserRepoToPerm) \
 
            .filter(UserRepoToPerm.user == user) \
 
            .filter(UserRepoToPerm.repository == repo) \
 
            .scalar()
 
        if obj is None:
 
            # create new !
 
            obj = UserRepoToPerm()
 
        obj.repository = repo
 
        obj.user = user
 
        obj.permission = permission
 
        self.sa.add(obj)
 
        log.debug('Granted perm %s to %s on %s', perm, user, repo)
 
        return obj
 

	
 
    def revoke_user_permission(self, repo, user):
 
        """
 
        Revoke permission for user on given repository
 

	
 
        :param repo: Instance of Repository, repository_id, or repository name
 
        :param user: Instance of User, user_id or username
 
        """
 

	
 
        user = self._get_user(user)
 
        repo = self._get_repo(repo)
 

	
 
        obj = self.sa.query(UserRepoToPerm) \
 
            .filter(UserRepoToPerm.repository == repo) \
 
            .filter(UserRepoToPerm.user == user) \
 
            .scalar()
 
        if obj is not None:
 
            self.sa.delete(obj)
 
            log.debug('Revoked perm on %s on %s', repo, user)
 

	
 
    def grant_user_group_permission(self, repo, group_name, perm):
 
        """
 
        Grant permission for user group on given repository, or update
 
        existing one if found
 

	
 
        :param repo: Instance of Repository, repository_id, or repository name
 
        :param group_name: Instance of UserGroup, users_group_id,
 
            or user group name
 
        :param perm: Instance of Permission, or permission_name
 
        """
 
        repo = self._get_repo(repo)
 
        group_name = self._get_user_group(group_name)
 
        permission = self._get_perm(perm)
 

	
 
        # check if we have that permission already
 
        obj = self.sa.query(UserGroupRepoToPerm) \
 
            .filter(UserGroupRepoToPerm.users_group == group_name) \
 
            .filter(UserGroupRepoToPerm.repository == repo) \
 
            .scalar()
 

	
 
        if obj is None:
 
            # create new
 
            obj = UserGroupRepoToPerm()
 

	
 
        obj.repository = repo
 
        obj.users_group = group_name
 
        obj.permission = permission
 
        self.sa.add(obj)
 
        log.debug('Granted perm %s to %s on %s', perm, group_name, repo)
 
        return obj
 

	
 
    def revoke_user_group_permission(self, repo, group_name):
 
        """
 
        Revoke permission for user group on given repository
 

	
 
        :param repo: Instance of Repository, repository_id, or repository name
 
        :param group_name: Instance of UserGroup, users_group_id,
 
            or user group name
 
        """
 
        repo = self._get_repo(repo)
 
        group_name = self._get_user_group(group_name)
 

	
 
        obj = self.sa.query(UserGroupRepoToPerm) \
 
            .filter(UserGroupRepoToPerm.repository == repo) \
 
            .filter(UserGroupRepoToPerm.users_group == group_name) \
 
            .scalar()
 
        if obj is not None:
 
            self.sa.delete(obj)
 
            log.debug('Revoked perm to %s on %s', repo, group_name)
 

	
 
    def delete_stats(self, repo_name):
 
        """
 
        removes stats for given repo
 

	
 
        :param repo_name:
 
        """
 
        repo = self._get_repo(repo_name)
 
        try:
 
            obj = self.sa.query(Statistics) \
 
                .filter(Statistics.repository == repo).scalar()
 
            if obj is not None:
 
                self.sa.delete(obj)
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            raise
 

	
 
    def _create_filesystem_repo(self, repo_name, repo_type, repo_group,
 
                                clone_uri=None, repo_store_location=None):
 
        """
 
        Makes repository on filesystem. Operation is group aware, meaning that it will create
 
        a repository within a group, and alter the paths accordingly to the group location.
 

	
 
        :param repo_name:
 
        :param alias:
 
        :param parent:
 
        :param clone_uri:
 
        :param repo_store_location:
 
        """
 
        from kallithea.lib.utils import is_valid_repo, is_valid_repo_group
 
        from kallithea.model.scm import ScmModel
 

	
 
        if '/' in repo_name:
 
            raise ValueError('repo_name must not contain groups got `%s`' % repo_name)
 

	
 
        if isinstance(repo_group, RepoGroup):
 
            new_parent_path = os.sep.join(repo_group.full_path_splitted)
 
        else:
 
            new_parent_path = repo_group or ''
 

	
 
        if repo_store_location:
 
            _paths = [repo_store_location]
 
        else:
 
            _paths = [self.repos_path, new_parent_path, repo_name]
 
            # we need to make it str for mercurial
 
        repo_path = os.path.join(*map(lambda x: safe_str(x), _paths))
 

	
 
        # check if this path is not a repository
 
        if is_valid_repo(repo_path, self.repos_path):
 
            raise Exception('This path %s is a valid repository' % repo_path)
 

	
 
        # check if this path is a group
 
        if is_valid_repo_group(repo_path, self.repos_path):
 
            raise Exception('This path %s is a valid group' % repo_path)
 

	
 
        log.info('creating repo %s in %s from url: `%s`',
 
            repo_name, safe_unicode(repo_path),
 
            obfuscate_url_pw(clone_uri))
 

	
 
        backend = get_backend(repo_type)
 

	
 
        if repo_type == 'hg':
 
            baseui = make_ui('db', clear_session=False)
 
            # patch and reset hooks section of UI config to not run any
 
            # hooks on creating remote repo
 
            for k, v in baseui.configitems('hooks'):
 
                baseui.setconfig('hooks', k, None)
 

	
 
            repo = backend(repo_path, create=True, src_url=clone_uri, baseui=baseui)
 
        elif repo_type == 'git':
 
            repo = backend(repo_path, create=True, src_url=clone_uri, bare=True)
 
            # add kallithea hook into this repo
 
            ScmModel().install_git_hooks(repo=repo)
 
        else:
 
            raise Exception('Not supported repo_type %s expected hg/git' % repo_type)
 

	
 
        log.debug('Created repo %s with %s backend',
 
                  safe_unicode(repo_name), safe_unicode(repo_type))
 
        return repo
 

	
 
    def _rename_filesystem_repo(self, old, new):
 
        """
 
        renames repository on filesystem
 

	
 
        :param old: old name
 
        :param new: new name
 
        """
 
        log.info('renaming repo from %s to %s', old, new)
 

	
 
        old_path = safe_str(os.path.join(self.repos_path, old))
 
        new_path = safe_str(os.path.join(self.repos_path, new))
 
        if os.path.isdir(new_path):
 
            raise Exception(
 
                'Was trying to rename to already existing dir %s' % new_path
 
            )
 
        shutil.move(old_path, new_path)
 

	
 
    def _delete_filesystem_repo(self, repo):
 
        """
 
        removes repo from filesystem, the removal is actually done by
 
        renaming dir to a 'rm__*' prefix which Kallithea will skip.
 
        It can be undeleted later by reverting the rename.
 

	
 
        :param repo: repo object
 
        """
 
        rm_path = safe_str(os.path.join(self.repos_path, repo.repo_name))
 
        log.info("Removing %s", rm_path)
 

	
 
        _now = datetime.now()
 
        _ms = str(_now.microsecond).rjust(6, '0')
 
        _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
 
                             repo.just_name)
 
        if repo.group:
 
            args = repo.group.full_path_splitted + [_d]
 
            _d = os.path.join(*args)
 
        shutil.move(rm_path, safe_str(os.path.join(self.repos_path, _d)))
kallithea/public/js/base.js
Show inline comments
 
@@ -1021,911 +1021,848 @@ var readNotification = function(url, not
 
    var failure = function(o){
 
            alert("readNotification failure");
 
        };
 
    var postData = {'_method': 'put'};
 
    var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
 
    ajaxPOST(sUrl, postData, success, failure);
 
};
 

	
 
/**
 
 * Autocomplete functionality
 
 */
 

	
 
// Custom search function for the DataSource of users
 
var autocompleteMatchUsers = function (sQuery, myUsers) {
 
    // 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++) {
 
        var 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;
 
};
 

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

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

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

	
 
// Return html snippet for showing the provided gravatar url
 
var gravatar = function(gravatar_lnk, size, cssclass) {
 
    if (!gravatar_lnk) {
 
        return '';
 
    }
 
    if (gravatar_lnk == 'default') {
 
        return '<i class="icon-user {1}" style="font-size: {0}px;"></i>'.format(size, cssclass);
 
    }
 
    return '<img alt="" class="{2}" style="width: {0}px; height: {0}px" src="{1}"/>'.format(size, gravatar_lnk, cssclass);
 
}
 

	
 
var autocompleteGravatar = function(res, gravatar_lnk, size, group) {
 
    var elem;
 
    if (group !== undefined) {
 
        elem = '<i class="perm-gravatar-ac icon-users"></i>';
 
    } else {
 
        elem = gravatar(gravatar_lnk, size, "perm-gravatar-ac");
 
    }
 
    return '<div class="ac-container-wrap">{0}{1}</div>'.format(elem, res);
 
}
 

	
 
// Custom formatter to highlight the matching letters
 
var autocompleteFormatter = function (oResultData, sQuery, sResultMatch) {
 
    var query = sQuery.toLowerCase();
 

	
 
    // group
 
    if (oResultData.grname != undefined) {
 
        var grname = oResultData.grname;
 
        var grmembers = oResultData.grmembers;
 
        var grnameMatchIndex = grname.toLowerCase().indexOf(query);
 
        var grprefix = "{0}: ".format(_TM['Group']);
 
        var grsuffix = " ({0} {1})".format(grmembers, _TM['members']);
 

	
 
        if (grnameMatchIndex > -1) {
 
            return autocompleteGravatar(grprefix + autocompleteHighlightMatch(grname, query, grnameMatchIndex) + grsuffix, null, null, true);
 
        }
 
        return autocompleteGravatar(grprefix + oResultData.grname + grsuffix, null, null, true);
 

	
 
    // users
 
    } else 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, displayname;
 

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

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

	
 
        if (nnameMatchIndex > -1) {
 
            displaynname = autocompleteHighlightMatch(nname, query, nnameMatchIndex);
 
        } else {
 
            displaynname = nname;
 
        }
 

	
 
        displayname = displaynname;
 
        if (displayfname && displaylname) {
 
            displayname = "{0} {1} ({2})".format(displayfname, displaylname, displayname);
 
        }
 

	
 
        return autocompleteGravatar(displayname, oResultData.gravatar_lnk, oResultData.gravatar_size);
 
    } else {
 
        return '';
 
    }
 
};
 

	
 
// Generate a basic autocomplete instance that can be tweaked further by the caller
 
var autocompleteCreate = function ($inputElement, $container, matchFunc) {
 
    var datasource = new YAHOO.util.FunctionDataSource(matchFunc);
 

	
 
    var autocomplete = new YAHOO.widget.AutoComplete($inputElement[0], $container[0], datasource);
 
    autocomplete.useShadow = false;
 
    autocomplete.resultTypeList = false;
 
    autocomplete.animVert = false;
 
    autocomplete.animHoriz = false;
 
    autocomplete.animSpeed = 0.1;
 
    autocomplete.formatResult = autocompleteFormatter;
 

	
 
    return autocomplete;
 
}
 

	
 
var SimpleUserAutoComplete = function ($inputElement, $container, users_list) {
 

	
 
    var matchUsers = function (sQuery) {
 
        return autocompleteMatchUsers(sQuery, users_list);
 
    }
 

	
 
    var userAC = autocompleteCreate($inputElement, $container, matchUsers);
 

	
 
    // Handler for selection of an entry
 
    var itemSelectHandler = 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
 
        myAC.getInputEl().value = oData.nname;
 
    };
 
    userAC.itemSelectEvent.subscribe(itemSelectHandler);
 
}
 

	
 
var MembersAutoComplete = function ($inputElement, $container, users_list, groups_list) {
 

	
 
    var matchAll = function (sQuery) {
 
        var u = autocompleteMatchUsers(sQuery, users_list);
 
        var g = autocompleteMatchGroups(sQuery, groups_list);
 
        return u.concat(g);
 
    };
 

	
 
    var membersAC = autocompleteCreate($inputElement, $container, matchAll);
 

	
 
    // Handler for selection of an entry
 
    var itemSelectHandler = function (sType, aArgs) {
 
        var nextId = $inputElement.prop('id').split('perm_new_member_name_')[1];
 
        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) {
 
            //users
 
            myAC.getInputEl().value = oData.nname;
 
            $('#perm_new_member_type_'+nextId).val('user');
 
        } else {
 
            //groups
 
            myAC.getInputEl().value = oData.grname;
 
            $('#perm_new_member_type_'+nextId).val('users_group');
 
        }
 
    };
 
    membersAC.itemSelectEvent.subscribe(itemSelectHandler);
 
}
 

	
 
var MentionsAutoComplete = function ($inputElement, $container, users_list) {
 

	
 
    var matchUsers = function (sQuery) {
 
            var org_sQuery = sQuery;
 
            if(this.mentionQuery == null){
 
                return []
 
            }
 
            sQuery = this.mentionQuery;
 
            return autocompleteMatchUsers(sQuery, users_list);
 
    }
 

	
 
    var mentionsAC = autocompleteCreate($inputElement, $container, matchUsers);
 
    mentionsAC.suppressInputUpdate = true;
 
    // Overwrite formatResult to take into account mentionQuery
 
    mentionsAC.formatResult = function (oResultData, sQuery, sResultMatch) {
 
        var org_sQuery = sQuery;
 
        if (this.dataSource.mentionQuery != null) {
 
            sQuery = this.dataSource.mentionQuery;
 
        }
 
        return autocompleteFormatter(oResultData, sQuery, sResultMatch);
 
    }
 

	
 
    // Handler for selection of an entry
 
    if(mentionsAC.itemSelectEvent){
 
        mentionsAC.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
 
            //Replace the mention name with replaced
 
            var re = new RegExp();
 
            var org = myAC.getInputEl().value;
 
            var chunks = myAC.dataSource.chunks
 
            // replace middle chunk(the search term) with actuall  match
 
            chunks[1] = chunks[1].replace('@'+myAC.dataSource.mentionQuery,
 
                                          '@'+oData.nname+' ');
 
            myAC.getInputEl().value = chunks.join('');
 
            myAC.getInputEl().focus(); // Y U NO WORK !?
 
        });
 
    }
 

	
 
    // in this keybuffer we will gather current value of search !
 
    // since we need to get this just when someone does `@` then we do the
 
    // search
 
    mentionsAC.dataSource.chunks = [];
 
    mentionsAC.dataSource.mentionQuery = null;
 

	
 
    mentionsAC.get_mention = function(msg, max_pos) {
 
        var org = msg;
 
        // Must match utils2.py MENTIONS_REGEX.
 
        // Only matching on string up to cursor, so it must end with $
 
        var re = new RegExp('(?:^|[^a-zA-Z0-9])@([a-zA-Z0-9][-_.a-zA-Z0-9]*[a-zA-Z0-9])$');
 
        var chunks  = [];
 

	
 
        // cut first chunk until current pos
 
        var to_max = msg.substr(0, max_pos);
 
        var at_pos = Math.max(0,to_max.lastIndexOf('@')-1);
 
        var msg2 = to_max.substr(at_pos);
 

	
 
        chunks.push(org.substr(0,at_pos)); // prefix chunk
 
        chunks.push(msg2);                 // search chunk
 
        chunks.push(org.substr(max_pos));  // postfix chunk
 

	
 
        // clean up msg2 for filtering and regex match
 
        var msg2 = msg2.lstrip(' ').lstrip('\n');
 

	
 
        if(re.test(msg2)){
 
            var unam = re.exec(msg2)[1];
 
            return [unam, chunks];
 
        }
 
        return [null, null];
 
    };
 

	
 
    $inputElement.keyup(function(e){
 
            var currentMessage = $inputElement.val();
 
            var currentCaretPosition = $inputElement[0].selectionStart;
 

	
 
            var unam = mentionsAC.get_mention(currentMessage, currentCaretPosition);
 
            var curr_search = null;
 
            if(unam[0]){
 
                curr_search = unam[0];
 
            }
 

	
 
            mentionsAC.dataSource.chunks = unam[1];
 
            mentionsAC.dataSource.mentionQuery = curr_search;
 
        });
 
}
 

	
 
var addReviewMember = function(id,fname,lname,nname,gravatar_link,gravatar_size){
 
    var displayname = nname;
 
    if ((fname != "") && (lname != "")) {
 
        displayname = "{0} {1} ({2})".format(fname, lname, nname);
 
    }
 
    var gravatarelm = gravatar(gravatar_link, gravatar_size, "");
 
    // WARNING: the HTML below is duplicate with
 
    // kallithea/templates/pullrequests/pullrequest_show.html
 
    // If you change something here it should be reflected in the template too.
 
    var element = (
 
        '     <li id="reviewer_{2}">\n'+
 
        '       <div class="reviewers_member">\n'+
 
        '           <div class="reviewer_status tooltip" title="not_reviewed">\n'+
 
        '             <i class="icon-circle changeset-status-not_reviewed"></i>\n'+
 
        '           </div>\n'+
 
        (gravatarelm ?
 
        '         <div class="reviewer_gravatar gravatar">{0}</div>\n' :
 
        '')+
 
        '         <div style="float:left;">{1}</div>\n'+
 
        '         <input type="hidden" value="{2}" name="review_members" />\n'+
 
        '         <div class="reviewer_member_remove action_button" onclick="removeReviewMember({2})">\n'+
 
        '             <i class="icon-minus-circled"></i>\n'+
 
        '         </div> (add not saved)\n'+
 
        '       </div>\n'+
 
        '     </li>\n'
 
        ).format(gravatarelm, displayname, id);
 
    // check if we don't have this ID already in
 
    var ids = [];
 
    $('#review_members').find('li').each(function() {
 
            ids.push(this.id);
 
        });
 
    if(ids.indexOf('reviewer_'+id) == -1){
 
        //only add if it's not there
 
        $('#review_members').append(element);
 
    }
 
}
 

	
 
var removeReviewMember = function(reviewer_id, repo_name, pull_request_id){
 
    var $li = $('#reviewer_{0}'.format(reviewer_id));
 
    $li.find('div div').css("text-decoration", "line-through");
 
    $li.find('input').prop('name', 'review_members_removed');
 
    $li.find('.reviewer_member_remove').replaceWith('&nbsp;(remove not saved)');
 
}
 

	
 
/* activate auto completion of users as PR reviewers */
 
var PullRequestAutoComplete = function ($inputElement, $container, users_list) {
 

	
 
    var matchUsers = function (sQuery) {
 
        return autocompleteMatchUsers(sQuery, users_list);
 
    };
 

	
 
    var reviewerAC = autocompleteCreate($inputElement, $container, matchUsers);
 
    reviewerAC.suppressInputUpdate = true;
 

	
 
    // Handler for selection of an entry
 
    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
 

	
 
            addReviewMember(oData.id, oData.fname, oData.lname, oData.nname,
 
                            oData.gravatar_lnk, oData.gravatar_size);
 
            myAC.getInputEl().value = '';
 
        });
 
    }
 
}
 

	
 
/**
 
 * Activate .quick_repo_menu
 
 */
 
var quick_repo_menu = function(){
 
    $(".quick_repo_menu").mouseenter(function(e) {
 
            var $menu = $(e.currentTarget).children().first().children().first();
 
            if($menu.hasClass('hidden')){
 
                $menu.removeClass('hidden').addClass('active');
 
                $(e.currentTarget).removeClass('hidden').addClass('active');
 
            }
 
        });
 
    $(".quick_repo_menu").mouseleave(function(e) {
 
            var $menu = $(e.currentTarget).children().first().children().first();
 
            if($menu.hasClass('active')){
 
                $menu.removeClass('active').addClass('hidden');
 
                $(e.currentTarget).removeClass('active').addClass('hidden');
 
            }
 
        });
 
};
 

	
 

	
 
/**
 
 * TABLE SORTING
 
 */
 

	
 
var revisionSort = function(a, b, desc, field) {
 
    var a_ = parseInt(a.getData('last_rev_raw') || 0);
 
    var b_ = parseInt(b.getData('last_rev_raw') || 0);
 

	
 
    return YAHOO.util.Sort.compare(a_, b_, desc);
 
};
 

	
 
var ageSort = function(a, b, desc, field) {
 
    // data is like: <span class="tooltip" date="2014-06-04 18:18:55.325474" title="Wed, 04 Jun 2014 18:18:55">1 day and 23 hours ago</span>
 
    var a_ = $(a.getData(field)).attr('date');
 
    var b_ = $(b.getData(field)).attr('date');
 

	
 
    return YAHOO.util.Sort.compare(a_, b_, desc);
 
};
 

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

	
 
    return YAHOO.util.Sort.compare(a_, b_, desc);
 
};
 

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

	
 
    return YAHOO.util.Sort.compare(a_, b_, desc);
 
};
 

	
 
var dateSort = function(a, b, desc, field) {
 
    var a_ = parseFloat(a.getData('raw_date') || 0);
 
    var b_ = parseFloat(b.getData('raw_date') || 0);
 

	
 
    return YAHOO.util.Sort.compare(a_, b_, desc);
 
};
 

	
 
var addPermAction = function(_html, users_list, groups_list){
 
    var $last_node = $('.last_new_member').last(); // empty tr between last and add
 
    var next_id = $('.new_members').length;
 
    $last_node.before($('<tr class="new_members">').append(_html.format(next_id)));
 
    MembersAutoComplete($("#perm_new_member_name_"+next_id),
 
            $("#perm_container_"+next_id), users_list, groups_list);
 
}
 

	
 
function ajaxActionRevokePermission(url, obj_id, obj_type, field_id, extra_data) {
 
    var success = function (o) {
 
            $('#' + field_id).remove();
 
        };
 
    var failure = function (o) {
 
            alert(_TM['Failed to revoke permission'] + ": " + o.status);
 
        };
 
    var query_params = {
 
        '_method': 'delete'
 
    }
 
    // put extra data into POST
 
    if (extra_data !== undefined && (typeof extra_data === 'object')){
 
        for(var 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';
 
    }
 

	
 
    ajaxPOST(url, query_params, success, failure);
 
};
 

	
 
/* Multi selectors */
 

	
 
var MultiSelectWidget = function(selected_id, available_id, form_id){
 
    var $availableselect = $('#' + available_id);
 
    var $selectedselect = $('#' + selected_id);
 

	
 
    //fill available only with those not in selected
 
    var $selectedoptions = $selectedselect.children('option');
 
    $availableselect.children('option').filter(function(i, e){
 
            for(var j = 0, node; node = $selectedoptions[j]; j++){
 
                if(node.value == e.value){
 
                    return true;
 
                }
 
            }
 
            return false;
 
        }).remove();
 

	
 
    $('#add_element').click(function(e){
 
            $selectedselect.append($availableselect.children('option:selected'));
 
        });
 
    $('#remove_element').click(function(e){
 
            $availableselect.append($selectedselect.children('option:selected'));
 
        });
 

	
 
    $('#'+form_id).submit(function(){
 
            $selectedselect.children('option').each(function(i, e){
 
                e.selected = 'selected';
 
            });
 
        });
 
}
 

	
 
// custom paginator
 
var YUI_paginator = function(links_per_page, containers){
 

	
 
    (function () {
 

	
 
        var Paginator = YAHOO.widget.Paginator,
 
            l         = YAHOO.lang,
 
            setId     = YAHOO.util.Dom.generateId;
 

	
 
        Paginator.ui.MyFirstPageLink = function (p) {
 
            this.paginator = p;
 

	
 
            p.subscribe('recordOffsetChange',this.update,this,true);
 
            p.subscribe('rowsPerPageChange',this.update,this,true);
 
            p.subscribe('totalRecordsChange',this.update,this,true);
 
            p.subscribe('destroy',this.destroy,this,true);
 

	
 
            // TODO: make this work
 
            p.subscribe('firstPageLinkLabelChange',this.update,this,true);
 
            p.subscribe('firstPageLinkClassChange',this.update,this,true);
 
        };
 

	
 
        Paginator.ui.MyFirstPageLink.init = function (p) {
 
            p.setAttributeConfig('firstPageLinkLabel', {
 
                value : 1,
 
                validator : l.isString
 
            });
 
            p.setAttributeConfig('firstPageLinkClass', {
 
                value : 'yui-pg-first',
 
                validator : l.isString
 
            });
 
            p.setAttributeConfig('firstPageLinkTitle', {
 
                value : 'First Page',
 
                validator : l.isString
 
            });
 
        };
 

	
 
        // Instance members and methods
 
        Paginator.ui.MyFirstPageLink.prototype = {
 
            current   : null,
 
            leftmost_page: null,
 
            rightmost_page: null,
 
            link      : null,
 
            span      : null,
 
            dotdot    : null,
 
            getPos    : function(cur_page, max_page, items){
 
                var edge = parseInt(items / 2) + 1;
 
                if (cur_page <= edge){
 
                    var radius = Math.max(parseInt(items / 2), items - cur_page);
 
                }
 
                else if ((max_page - cur_page) < edge) {
 
                    var radius = (items - 1) - (max_page - cur_page);
 
                }
 
                else{
 
                    var radius = parseInt(items / 2);
 
                }
 

	
 
                var left = Math.max(1, (cur_page - (radius)));
 
                var right = Math.min(max_page, cur_page + (radius));
 
                return [left, cur_page, right]
 
            },
 
            render : function (id_base) {
 
                var p      = this.paginator,
 
                    c      = p.get('firstPageLinkClass'),
 
                    label  = p.get('firstPageLinkLabel'),
 
                    title  = p.get('firstPageLinkTitle');
 

	
 
                this.link     = document.createElement('a');
 
                this.span     = document.createElement('span');
 
                $(this.span).hide();
 

	
 
                var _pos = this.getPos(p.getCurrentPage(), p.getTotalPages(), 5);
 
                this.leftmost_page = _pos[0];
 
                this.rightmost_page = _pos[2];
 

	
 
                setId(this.link, id_base + '-first-link');
 
                this.link.href      = '#';
 
                this.link.className = c;
 
                this.link.innerHTML = label;
 
                this.link.title     = title;
 
                YUE.on(this.link,'click',this.onClick,this,true);
 

	
 
                setId(this.span, id_base + '-first-span');
 
                this.span.className = c;
 
                this.span.innerHTML = label;
 

	
 
                this.current = p.getCurrentPage() > 1 ? this.link : this.span;
 
                return this.current;
 
            },
 
            update : function (e) {
 
                var p      = this.paginator;
 
                var _pos   = this.getPos(p.getCurrentPage(), p.getTotalPages(), 5);
 
                this.leftmost_page = _pos[0];
 
                this.rightmost_page = _pos[2];
 

	
 
                if (e && e.prevValue === e.newValue) {
 
                    return;
 
                }
 

	
 
                var par = this.current ? this.current.parentNode : null;
 
                if (this.leftmost_page > 1) {
 
                    if (par && this.current === this.span) {
 
                        par.replaceChild(this.link,this.current);
 
                        this.current = this.link;
 
                    }
 
                } else {
 
                    if (par && this.current === this.link) {
 
                        par.replaceChild(this.span,this.current);
 
                        this.current = this.span;
 
                    }
 
                }
 
            },
 
            destroy : function () {
 
                YUE.purgeElement(this.link);
 
                this.current.parentNode.removeChild(this.current);
 
                this.link = this.span = null;
 
            },
 
            onClick : function (e) {
 
                YUE.stopEvent(e);
 
                this.paginator.setPage(1);
 
            }
 
        };
 

	
 
        })();
 

	
 
    (function () {
 

	
 
        var Paginator = YAHOO.widget.Paginator,
 
            l         = YAHOO.lang,
 
            setId     = YAHOO.util.Dom.generateId;
 

	
 
        Paginator.ui.MyLastPageLink = function (p) {
 
            this.paginator = p;
 

	
 
            p.subscribe('recordOffsetChange',this.update,this,true);
 
            p.subscribe('rowsPerPageChange',this.update,this,true);
 
            p.subscribe('totalRecordsChange',this.update,this,true);
 
            p.subscribe('destroy',this.destroy,this,true);
 

	
 
            // TODO: make this work
 
            p.subscribe('lastPageLinkLabelChange',this.update,this,true);
 
            p.subscribe('lastPageLinkClassChange', this.update,this,true);
 
        };
 

	
 
        Paginator.ui.MyLastPageLink.init = function (p) {
 
            p.setAttributeConfig('lastPageLinkLabel', {
 
                value : -1,
 
                validator : l.isString
 
            });
 
            p.setAttributeConfig('lastPageLinkClass', {
 
                value : 'yui-pg-last',
 
                validator : l.isString
 
            });
 
            p.setAttributeConfig('lastPageLinkTitle', {
 
                value : 'Last Page',
 
                validator : l.isString
 
            });
 

	
 
        };
 

	
 
        Paginator.ui.MyLastPageLink.prototype = {
 

	
 
            current   : null,
 
            leftmost_page: null,
 
            rightmost_page: null,
 
            link      : null,
 
            span      : null,
 
            dotdot    : null,
 
            na        : null,
 
            getPos    : function(cur_page, max_page, items){
 
                var edge = parseInt(items / 2) + 1;
 
                if (cur_page <= edge){
 
                    var radius = Math.max(parseInt(items / 2), items - cur_page);
 
                }
 
                else if ((max_page - cur_page) < edge) {
 
                    var radius = (items - 1) - (max_page - cur_page);
 
                }
 
                else{
 
                    var radius = parseInt(items / 2);
 
                }
 

	
 
                var left = Math.max(1, (cur_page - (radius)));
 
                var right = Math.min(max_page, cur_page + (radius));
 
                return [left, cur_page, right]
 
            },
 
            render : function (id_base) {
 
                var p      = this.paginator,
 
                    c      = p.get('lastPageLinkClass'),
 
                    label  = p.get('lastPageLinkLabel'),
 
                    last   = p.getTotalPages(),
 
                    title  = p.get('lastPageLinkTitle');
 

	
 
                var _pos = this.getPos(p.getCurrentPage(), p.getTotalPages(), 5);
 
                this.leftmost_page = _pos[0];
 
                this.rightmost_page = _pos[2];
 

	
 
                this.link = document.createElement('a');
 
                this.span = document.createElement('span');
 
                $(this.span).hide();
 

	
 
                this.na   = this.span.cloneNode(false);
 

	
 
                setId(this.link, id_base + '-last-link');
 
                this.link.href      = '#';
 
                this.link.className = c;
 
                this.link.innerHTML = label;
 
                this.link.title     = title;
 
                YUE.on(this.link,'click',this.onClick,this,true);
 

	
 
                setId(this.span, id_base + '-last-span');
 
                this.span.className = c;
 
                this.span.innerHTML = label;
 

	
 
                setId(this.na, id_base + '-last-na');
 

	
 
                if (this.rightmost_page < p.getTotalPages()){
 
                    this.current = this.link;
 
                }
 
                else{
 
                    this.current = this.span;
 
                }
 

	
 
                this.current.innerHTML = p.getTotalPages();
 
                return this.current;
 
            },
 

	
 
            update : function (e) {
 
                var p      = this.paginator;
 

	
 
                var _pos = this.getPos(p.getCurrentPage(), p.getTotalPages(), 5);
 
                this.leftmost_page = _pos[0];
 
                this.rightmost_page = _pos[2];
 

	
 
                if (e && e.prevValue === e.newValue) {
 
                    return;
 
                }
 

	
 
                var par   = this.current ? this.current.parentNode : null,
 
                    after = this.link;
 
                if (par) {
 

	
 
                    // only show the last page if the rightmost one is
 
                    // lower, so we don't have doubled entries at the end
 
                    if (!(this.rightmost_page < p.getTotalPages())){
 
                        after = this.span
 
                    }
 

	
 
                    if (this.current !== after) {
 
                        par.replaceChild(after,this.current);
 
                        this.current = after;
 
                    }
 
                }
 
                this.current.innerHTML = this.paginator.getTotalPages();
 

	
 
            },
 
            destroy : function () {
 
                YUE.purgeElement(this.link);
 
                this.current.parentNode.removeChild(this.current);
 
                this.link = this.span = null;
 
            },
 
            onClick : function (e) {
 
                YUE.stopEvent(e);
 
                this.paginator.setPage(this.paginator.getTotalPages());
 
            }
 
        };
 

	
 
        })();
 

	
 
    var pagi = new YAHOO.widget.Paginator({
 
        rowsPerPage: links_per_page,
 
        alwaysVisible: false,
 
        template : "{PreviousPageLink} {MyFirstPageLink} {PageLinks} {MyLastPageLink} {NextPageLink}",
 
        pageLinks: 5,
 
        containerClass: 'pagination-wh',
 
        currentPageClass: 'pager_curpage',
 
        pageLinkClass: 'pager_link',
 
        nextPageLinkLabel: '&gt;',
 
        previousPageLinkLabel: '&lt;',
 
        containers:containers
 
    });
 

	
 
    return pagi
 
}
 

	
 
var YUI_datatable = function(data, fields, columns, countnode, sortkey, rows){
 
    var myDataSource = new YAHOO.util.DataSource(data);
 
    myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;
 
    myDataSource.responseSchema = {
 
        resultsList: "records",
 
        fields: fields
 
        };
 
    myDataSource.doBeforeCallback = function(req, raw, res, cb) {
 
        // This is the filter function
 
        var data     = res.results || [],
 
            filtered = [],
 
            i, l;
 

	
 
        if (req) {
 
            req = req.toLowerCase();
 
            for (i = 0; i<data.length; i++) {
 
                var pos = data[i].raw_name.toLowerCase().indexOf(req);
 
                if (pos != -1) {
 
                    filtered.push(data[i]);
 
                }
 
            }
 
            res.results = filtered;
 
        }
 
        $(countnode).html(res.results.length);
 
        return res;
 
    }
 

	
 
    var myDataTable = new YAHOO.widget.DataTable("datatable_list_wrap", columns, myDataSource, {
 
        sortedBy: {key:sortkey, dir:"asc"},
 
        paginator: YUI_paginator(rows !== undefined && rows ? rows : 25, ['user-paginator']),
 
        MSG_SORTASC: _TM['MSG_SORTASC'],
 
        MSG_SORTDESC: _TM['MSG_SORTDESC'],
 
        MSG_EMPTY: _TM['MSG_EMPTY'],
 
        MSG_ERROR: _TM['MSG_ERROR'],
 
        MSG_LOADING: _TM['MSG_LOADING']
 
        });
 
    myDataTable.subscribe('postRenderEvent',function(oArgs) {
 
        tooltip_activate();
 
        quick_repo_menu();
 
        });
 

	
 
    var filterTimeout = null;
 
    var $q_filter = $('#q_filter');
 

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

	
 
        // Reset sort
 
        var state = myDataTable.getState();
 
        state.sortedBy = {key:sortkey, dir:YAHOO.widget.DataTable.CLASS_ASC};
 

	
 
        // Get filtered data
 
        myDataSource.sendRequest($q_filter.val(), {
 
            success : myDataTable.onDataReturnInitializeTable,
 
            failure : myDataTable.onDataReturnInitializeTable,
 
            scope   : myDataTable,
 
            argument: state});
 
        };
 

	
 
    $q_filter.click(function(){
 
            if(!$q_filter.hasClass('loaded')){
 
                //TODO: load here full list later to do search within groups
 
                $q_filter.addClass('loaded');
 
            }
 
        });
 

	
 
    $q_filter.keyup(function (e) {
 
            clearTimeout(filterTimeout);
 
            filterTimeout = setTimeout(updateFilter, 600);
 
        });
 
}
 

	
 
/**
 
 Branch Sorting callback for select2, modifying the filtered result so prefix
 
 matches come before matches in the line.
 
 **/
 
var branchSort = function(results, container, query) {
 
    if (query.term) {
 
        return results.sort(function (a, b) {
 
            // Put closed branches after open ones (a bit of a hack ...)
 
            var aClosed = a.text.indexOf("(closed)") > -1,
 
                bClosed = b.text.indexOf("(closed)") > -1;
 
            if (aClosed && !bClosed) {
 
                return 1;
 
            }
 
            if (bClosed && !aClosed) {
 
                return -1;
 
            }
 

	
 
            // Put prefix matches before matches in the line
 
            var aPos = a.text.toLowerCase().indexOf(query.term.toLowerCase()),
 
                bPos = b.text.toLowerCase().indexOf(query.term.toLowerCase());
 
            if (aPos === 0 && bPos !== 0) {
 
                return -1;
 
            }
 
            if (bPos === 0 && aPos !== 0) {
 
                return 1;
 
            }
 

	
 
            // Default sorting
 
            if (a.text > b.text) {
 
                return 1;
 
            }
 
            if (a.text < b.text) {
 
                return -1;
 
            }
 
            return 0;
 
        });
 
    }
 
    return results;
 
};
 

	
 
var prefixFirstSort = function(results, container, query) {
 
    if (query.term) {
 
        return results.sort(function (a, b) {
 
            // if parent node, no sorting
 
            if (a.children != undefined || b.children != undefined) {
 
                return 0;
 
            }
 

	
 
            // Put prefix matches before matches in the line
 
            var aPos = a.text.toLowerCase().indexOf(query.term.toLowerCase()),
 
                bPos = b.text.toLowerCase().indexOf(query.term.toLowerCase());
 
            if (aPos === 0 && bPos !== 0) {
 
                return -1;
 
            }
 
            if (bPos === 0 && aPos !== 0) {
 
                return 1;
 
            }
 

	
 
            // Default sorting
 
            if (a.text > b.text) {
 
                return 1;
 
            }
 
            if (a.text < b.text) {
 
                return -1;
 
            }
 
            return 0;
 
        });
 
    }
 
    return results;
 
};
 

	
 
/* Helper for jQuery DataTables */
 

	
 
var updateRowCountCallback = function updateRowCountCallback($elem, onlyDisplayed) {
 
    return function drawCallback() {
 
        var info = this.api().page.info(),
 
            count = onlyDisplayed === true ? info.recordsDisplay : info.recordsTotal;
 
        $elem.html(count);
 
    }
 
};
kallithea/templates/admin/repo_groups/repo_groups.html
Show inline comments
 
## -*- coding: utf-8 -*-
 
<%inherit file="/base/base.html"/>
 

	
 
<%block name="title">
 
    ${_('Repository Groups Administration')}
 
</%block>
 

	
 
<%def name="breadcrumbs_links()">
 
    <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
 
    ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; <span id="repo_group_count">0</span> ${_('Repository Groups')}
 
</%def>
 

	
 

	
 
<%block name="header_menu">
 
    ${self.menu('admin')}
 
</%block>
 

	
 
<%def name="main()">
 
<div class="box">
 
    <!-- box / title -->
 
    <div class="title">
 
        ${self.breadcrumbs()}
 
        <ul class="links">
 
            %if h.HasPermissionAny('hg.admin')():
 
             <li>
 
               <a href="${h.url('new_repos_group')}" class="btn btn-small btn-success"><i class="icon-plus"></i> ${_('Add Repository Group')}</a>
 
             </li>
 
            %endif
 
        </ul>
 
    </div>
 
    <!-- end box / title -->
 
    <div class="table-grid table yui-skin-sam" id="datatable_list_wrap"></div>
 
    <div class="table">
 
        <table id="datatable_list_wrap"></table>
 
    </div>
 
    <div id="user-paginator" style="padding: 0px 0px 0px 20px"></div>
 
</div>
 
<script>
 
  var data = ${c.data|n};
 
  var fields = [
 
    {key: "group_name"},
 
    {key: "raw_name"},
 
    {key: "desc"},
 
    {key: "repos"},
 
    {key: "owner"},
 
    {key: "action"}
 
  ];
 
  var column_defs = [
 
    {key:"group_name",label:"${_('Name')}",sortable:true, sortOptions: { sortFunction: nameSort }},
 
    {key:"desc",label:"${_('Description')}",sortable:true},
 
    {key:"repos",label:"${_('Number of Top-level Repositories')}",sortable:true},
 
    {key:"owner",label:"${_('Owner')}",sortable:true},
 
    {key:"action",label:"${_('Action')}",sortable:false}
 
  ];
 
  var counter = YUD.get('repo_group_count');
 
  var sort_key = "group_name";
 
  YUI_datatable(data, fields, column_defs, counter, sort_key, ${c.visual.admin_grid_items});
 
  var myDataTable = $("#datatable_list_wrap").DataTable({
 
        data: data.records,
 
        columns: [
 
            {data: "raw_name", visible: false, searchable: false},
 
            {data: "group_name", orderData: 0, title: "${_('Name')}"},
 
            {data: "desc", title: "${_('Description')}", searchable: false},
 
            {data: "repos", title: "${_('Number of Top-level Repositories')}", searchable: false},
 
            {data: "owner", title: "${_('Owner')}", searchable: false},
 
            {data: "action", title: "${_('Action')}", sortable: false, searchable: false}
 
        ],
 
        drawCallback: updateRowCountCallback($("#repo_group_count"))
 
    });
 

	
 
</script>
 
</%def>
kallithea/templates/admin/repos/repos.html
Show inline comments
 
## -*- coding: utf-8 -*-
 
<%inherit file="/base/base.html"/>
 

	
 
<%block name="title">
 
    ${_('Repositories Administration')}
 
</%block>
 

	
 
<%def name="breadcrumbs_links()">
 
    <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/> ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; <span id="repo_count">0</span> ${_('Repositories')}
 
    <span id="repo_count">0</span> ${_('Repositories')}
 
</%def>
 
<%block name="header_menu">
 
    ${self.menu('admin')}
 
</%block>
 
<%def name="main()">
 
<div class="box">
 

	
 
    <div class="title">
 
        ${self.breadcrumbs()}
 
        <ul class="links">
 
         %if h.HasPermissionAny('hg.admin','hg.create.repository')():
 
          <li>
 
            <a href="${h.url('new_repo')}" class="btn btn-small btn-success"><i class="icon-plus"></i> ${_('Add Repository')}</a>
 
          </li>
 
         %endif
 
        </ul>
 
    </div>
 
    <div class="table-grid table yui-skin-sam" id="datatable_list_wrap"></div>
 
    <div class="table">
 
        <table id="datatable_list_wrap"></table>
 
    </div>
 
    <div id="user-paginator" style="padding: 0px 0px 0px 20px"></div>
 

	
 

	
 
</div>
 
<script>
 
  var data = ${c.data|n};
 
  var fields = [
 
    {key:"menu"},
 
    {key:"raw_name"},
 
    {key:"name"},
 
    {key:"desc"},
 
    {key:"last_changeset"},
 
    {key:"last_rev_raw"},
 
    {key:"owner"},
 
    {key:"state"},
 
    {key:"action"}
 
  ];
 
  var column_defs = [
 
    {key:"menu",label:"",sortable:false,className:"quick_repo_menu hidden"},
 
    {key:"name",label:"${_('Name')}",sortable:true, sortOptions: { sortFunction: nameSort }},
 
    {key:"desc",label:"${_('Description')}",sortable:true},
 
    {key:"last_changeset",label:"${_('Tip')}",sortable:true, sortOptions: { sortFunction: revisionSort }},
 
    {key:"owner",label:"${_('Owner')}",sortable:true},
 
    {key:"state",label:"${_('State')}",sortable:true},
 
    {key:"action",label:"${_('Action')}",sortable:false}
 
  ];
 
  var counter = YUD.get('repo_count');
 
  var sort_key = "name";
 
  YUI_datatable(data, fields, column_defs, counter, sort_key, ${c.visual.admin_grid_items});
 
  var myDataTable = $("#datatable_list_wrap").DataTable({
 
        data: data.records,
 
        columns: [
 
            {data: "menu", sortable: false, className: "quick_repo_menu hidden", searchable: false},
 
            {data: "raw_name", visible: false, searchable: false},
 
            {data: "name", orderData: 1, title: "${_('Name')}"},
 
            {data: "desc", title: "${_('Description')}", searchable: false},
 
            {data: "last_rev_raw", visible: false, searchable: false},
 
            {data: "last_changeset", orderData: 4, title: "${_('Tip')}", searchable: false},
 
            {data: "owner", title: "${_('Owner')}", searchable: false},
 
            {data: "state", title: "${_('State')}", searchable: false},
 
            {data: "action", title: "${_('Action')}", sortable: false, searchable: false}
 
        ],
 
        drawCallback: updateRowCountCallback($("#repo_count"))
 
    });
 
</script>
 

	
 
</%def>
kallithea/templates/admin/user_groups/user_groups.html
Show inline comments
 
## -*- coding: utf-8 -*-
 
<%inherit file="/base/base.html"/>
 

	
 
<%block name="title">
 
    ${_('User Groups Administration')}
 
</%block>
 

	
 
<%def name="breadcrumbs_links()">
 
    <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
 
    ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; <span id="user_group_count">0</span> ${_('User Groups')}
 
</%def>
 

	
 
<%block name="header_menu">
 
    ${self.menu('admin')}
 
</%block>
 

	
 
<%def name="main()">
 
<div class="box">
 
    <!-- box / title -->
 
    <div class="title">
 
        ${self.breadcrumbs()}
 
        <ul class="links">
 
        %if h.HasPermissionAny('hg.admin', 'hg.usergroup.create.true')():
 
          <li>
 
            <a href="${h.url('new_users_group')}" class="btn btn-small btn-success"><i class="icon-plus"></i> ${_('Add User Group')}</a>
 
          </li>
 
        %endif
 
        </ul>
 
    </div>
 
    <!-- end box / title -->
 
    <div class="table-grid table yui-skin-sam" id="datatable_list_wrap"></div>
 
    <div class="table">
 
        <table id="datatable_list_wrap"></table>
 
    </div>
 
    <div id="user-paginator" style="padding: 0px 0px 0px 20px"></div>
 
</div>
 
<script>
 
  var data = ${c.data|n};
 
  var fields = [
 
    {key: "group_name"},
 
    {key: "raw_name"},
 
    {key: "desc"},
 
    {key: "members"},
 
    {key: "active"},
 
    {key: "owner"},
 
    {key: "action"}
 
  ];
 
  var column_defs = [
 
    {key:"group_name",label:"${_('Name')}",sortable:true, sortOptions: { sortFunction: nameSort }},
 
    {key:"desc",label:"${_('Description')}",sortable:true},
 
    {key:"members",label:"${_('Members')}",sortable:false},
 
    {key:"active",label:"${_('Active')}",sortable:true},
 
    {key:"owner",label:"${_('Owner')}",sortable:true},
 
    {key:"action",label:"${_('Action')}",sortable:false}
 
  ];
 
  var counter = YUD.get('user_group_count');
 
  var sort_key = "group_name";
 
  YUI_datatable(data, fields, column_defs, counter, sort_key, ${c.visual.admin_grid_items});
 
    var data = ${c.data|n};
 
    var $dataTable = $("#datatable_list_wrap").DataTable({
 
        data: data.records,
 
        columns: [
 
            {data: "raw_name", visible: false, searchable: false},
 
            {data: "group_name", title: "${_('Name')}", orderData: 0},
 
            {data: "desc", title: "${_('Description')}", searchable: false},
 
            {data: "members", title: "${_('Members')}", searchable: false},
 
            {data: "active", title: "${_('Active')}", searchable: false, 'sType': 'str'},
 
            {data: "owner", title: "${_('Owner')}", searchable: false},
 
            {data: "action", title: "${_('Action')}", searchable: false, sortable: false}
 
        ],
 
        order: [[1, "asc"]]
 
    });
 
</script>
 
</%def>
kallithea/templates/admin/users/users.html
Show inline comments
 
## -*- coding: utf-8 -*-
 
<%inherit file="/base/base.html"/>
 

	
 
<%block name="title">
 
    ${_('Users Administration')}
 
</%block>
 

	
 
<%def name="breadcrumbs_links()">
 
    <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
 
    ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; <span id="user_count">0</span> ${_('Users')}
 
</%def>
 

	
 
<%block name="header_menu">
 
    ${self.menu('admin')}
 
</%block>
 

	
 
<%def name="main()">
 
<div class="box">
 
    <!-- box / title -->
 
    <div class="title">
 
        ${self.breadcrumbs()}
 
        <ul class="links">
 
          <li>
 
            <a href="${h.url('new_user')}" class="btn btn-small btn-success"><i class="icon-plus"></i> ${_('Add User')}</a>
 
          </li>
 
        </ul>
 
    </div>
 
    <!-- end box / title -->
 
    <div class="table-grid table yui-skin-sam" id="datatable_list_wrap"></div>
 
    <div id="user-paginator" style="padding: 0px 0px 0px 20px"></div>
 
    <div class="table">
 
        <table id="datatable_list_wrap"></table>
 
    </div>
 
</div>
 

	
 
<script>
 
  var data = ${c.data|n};
 
  var fields = [
 
    {key: "gravatar"},
 
    {key: "raw_name"},
 
    {key: "username"},
 
    {key: "firstname"},
 
    {key: "lastname"},
 
    {key: "last_login"},
 
    {key: "last_login_raw"},
 
    {key: "active"},
 
    {key: "admin"},
 
    {key: "extern_type"},
 
    {key: "action"}
 
  ];
 
  var column_defs = [
 
    {key:"gravatar",label:"",sortable:false},
 
    {key:"username",label:"${_('Username')}",sortable:true},
 
    {key:"firstname",label:"${_('First Name')}",sortable:true},
 
    {key:"lastname",label:"${_('Last Name')}",sortable:true},
 
    {key:"last_login",label:"${_('Last Login')}",sortable:true, sortOptions: { sortFunction: lastLoginSort }},
 
    {key:"active",label:"${_('Active')}",sortable:true},
 
    {key:"admin",label:"${_('Admin')}",sortable:true},
 
    {key:"extern_type",label:"${_('Auth Type')}",sortable:true},
 
    {key:"action",label:"${_('Action')}",sortable:false}
 
  ];
 
  var counter = YUD.get('user_count');
 
  var sort_key = "username";
 
  YUI_datatable(data, fields, column_defs, counter, sort_key, ${c.visual.admin_grid_items});
 
    var data = ${c.data|n};
 
    var $dataTable = $("#datatable_list_wrap").DataTable({
 
        data: data.records,
 
        columns: [
 
            {data: "gravatar", sortable: false, searchable: false},
 
            {data: "username", title: "${_('Username')}"},
 
            {data: "firstname", title: "${_('First Name')}"},
 
            {data: "lastname", title: "${_('Last Name')}"},
 
            {data: "last_login_raw", visible: false, searchable: false},
 
            {data: "last_login", title: "${_('Last Login')}", orderData: 4, searchable: false},
 
            {data: "active", title: "${_('Active')}", searchable: false, 'sType': 'str'},
 
            {data: "admin", title: "${_('Admin')}", searchable: false, 'sType': 'str'},
 
            {data: "extern_type", title: "${_('Auth Type')}", searchable: false},
 
            {data: "action", title: "${_('Action')}", searchable: false, sortable: false}
 
        ],
 
        order: [[1, "asc"]],
 
        drawCallback: updateRowCountCallback($("#user_count"))
 
    });
 
</script>
 

	
 
</%def>
kallithea/templates/index_base.html
Show inline comments
 
<%page args="parent,group_name=''" />
 
    <div class="box">
 
        <!-- box / title -->
 
        <div class="title">
 
            <h5>
 
            <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/> ${parent.breadcrumbs()} <span id="repo_count">0</span> ${_('repositories')}
 
            </h5>
 
            %if c.authuser.username != 'default':
 
              <ul class="links">
 
                <li>
 
                <%
 
                    gr_name = c.group.group_name if c.group else None
 
                    # create repositories with write permission on group is set to true
 
                    create_on_write = h.HasPermissionAny('hg.create.write_on_repogroup.true')()
 
                    group_admin = h.HasRepoGroupPermissionAny('group.admin')(gr_name, 'can write into group index page')
 
                    group_write = h.HasRepoGroupPermissionAny('group.write')(gr_name, 'can write into group index page')
 
                %>
 
                %if h.HasPermissionAny('hg.admin','hg.create.repository')() or (group_admin or (group_write and create_on_write)):
 
                  %if c.group:
 
                        <a href="${h.url('new_repo',parent_group=c.group.group_id)}" class="btn btn-small"><i class="icon-plus"></i> ${_('Add Repository')}</a>
 
                        %if h.HasPermissionAny('hg.admin')() or h.HasRepoGroupPermissionAny('group.admin')(c.group.group_name):
 
                            <a href="${h.url('new_repos_group', parent_group=c.group.group_id)}" class="btn btn-small"><i class="icon-plus"></i> ${_('Add Repository Group')}</a>
 
                        %endif
 
                  %else:
 
                    <a href="${h.url('new_repo')}" class="btn btn-small"><i class="icon-plus"></i> ${_('Add Repository')}</a>
 
                    %if h.HasPermissionAny('hg.admin')():
 
                        <a href="${h.url('new_repos_group')}" class="btn btn-small"><i class="icon-plus"></i> ${_('Add Repository Group')}</a>
 
                    %endif
 
                  %endif
 
                %endif
 
                %if c.group and h.HasRepoGroupPermissionAny('group.admin')(c.group.group_name):
 
                    <a href="${h.url('edit_repo_group',group_name=c.group.group_name)}" title="${_('You have admin right to this group, and can edit it')}" class="btn btn-small"><i class="icon-pencil"></i> ${_('Edit Repository Group')}</a>
 
                %endif
 
                </li>
 
              </ul>
 
            %endif
 
        </div>
 
        <!-- end box / title -->
 
        <div class="table">
 
           % if c.groups:
 
            <div id='groups_list_wrap' class="yui-skin-sam">
 
              <table id="groups_list">
 
                  <thead>
 
                      <tr>
 
                          <th class="left"><a href="#">${_('Group Name')}</a></th>
 
                          <th class="left"><a href="#">${_('Description')}</a></th>
 
                          ##<th class="left"><a href="#">${_('Number of Repositories')}</a></th>
 
                      </tr>
 
                  </thead>
 

	
 
                  ## REPO GROUPS
 
                  % for gr in c.groups:
 
                    <tr>
 
                        <td>
 
                            <div class="dt_repo">
 
                              <a href="${url('repos_group_home',group_name=gr.group_name)}">
 
                                <i class="icon-folder"></i>
 
                                <span class="dt_repo_name">${gr.name}</span>
 
                              </a>
 
                            </div>
 
                        </td>
 
                        <td>${h.urlify_text(gr.group_description, stylize=c.visual.stylify_metatags)}</td>
 
                        ## this is commented out since for multi nested repos can be HEAVY!
 
                        ## in number of executed queries during traversing uncomment at will
 
                        ##<td><b>${gr.repositories_recursive_count}</b></td>
 
                    </tr>
 
                  % endfor
 
              </table>
 
            </div>
 
            <div id="group-user-paginator" style="padding: 0px 0px 0px 0px"></div>
 
            <div style="height: 20px"></div>
 
            % endif
 
            <div id="welcome" style="display:none;text-align:center">
 
                <h1><a href="${h.url('home')}">${c.site_name} ${c.kallithea_version}</a></h1>
 
            </div>
 
            <%cnt=0%>
 
            <%namespace name="dt" file="/data_table/_dt_elements.html"/>
 
            <div class="yui-skin-sam" id="repos_list_wrap"></div>
 
            <table class="yui-skin-sam" id="repos_list_wrap"></table>
 
            <div id="user-paginator" style="padding: 0px 0px 0px 0px"></div>
 
        </div>
 
    </div>
 

	
 
      <script>
 
        var data = ${c.data|n};
 
        var myDataSource = new YAHOO.util.DataSource(data);
 
        myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;
 

	
 
        myDataSource.responseSchema = {
 
            resultsList: "records",
 
            fields: [
 
               {key:"menu"},
 
               {key:"raw_name"},
 
               {key:"name"},
 
               {key:"desc"},
 
               {key:"last_change"},
 
               {key:"last_changeset"},
 
               {key:"last_rev_raw"},
 
               {key:"owner"},
 
               {key:"atom"}
 
            ]
 
         };
 
        myDataSource.doBeforeCallback = function(req,raw,res,cb) {
 
            // This is the filter function
 
            var data     = res.results || [],
 
                filtered = [],
 
                i,l;
 

	
 
            if (req) {
 
                req = req.toLowerCase();
 
                for (i = 0; i<data.length; i++) {
 
                    var pos = data[i].raw_name.toLowerCase().indexOf(req, ${len(group_name)});
 
                    if (pos != -1) {
 
                        filtered.push(data[i]);
 
                    }
 
                }
 
                res.results = filtered;
 
            }
 
            YUD.get('repo_count').innerHTML = res.results.length;
 
            return res;
 
        }
 

	
 
        // main table sorting
 
        var myColumnDefs = [
 
            {key:"menu",label:"",sortable:false,className:"quick_repo_menu hidden"},
 
            {key:"name",label:"${_('Name')}",sortable:true,
 
                sortOptions: { sortFunction: nameSort }},
 
            {key:"desc",label:"${_('Description')}",sortable:true},
 
            {key:"last_change",label:"${_('Last Change')}",sortable:true,
 
                sortOptions: { sortFunction: ageSort }},
 
            {key:"last_changeset",label:"${_('Tip')}",sortable:true,
 
                sortOptions: { sortFunction: revisionSort }},
 
            {key:"owner",label:"${_('Owner')}",sortable:true},
 
            {key:"atom",label:"",sortable:false}
 
        ];
 

	
 
        var myDataTable = new YAHOO.widget.DataTable("repos_list_wrap", myColumnDefs, myDataSource,{
 
          sortedBy:{key:"name",dir:"asc"},
 
          paginator: YUI_paginator(${c.visual.dashboard_items},['user-paginator']),
 

	
 
          MSG_SORTASC:"${_('Click to sort ascending')}",
 
          MSG_SORTDESC:"${_('Click to sort descending')}",
 
          MSG_EMPTY:"${_('No repositories found.')}",
 
          MSG_ERROR:"${_('Data error.')}",
 
          MSG_LOADING:"${_('Loading...')}"
 
        }
 
        );
 
        myDataTable.subscribe('postRenderEvent',function(oArgs) {
 
            tooltip_activate();
 
            quick_repo_menu();
 
        });
 

	
 
        var filterTimeout = null;
 

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

	
 
            // Reset sort
 
            var state = myDataTable.getState();
 
            state.sortedBy = {key:'name', dir:YAHOO.widget.DataTable.CLASS_ASC};
 

	
 
            // Get filtered data
 
            myDataSource.sendRequest(YUD.get('q_filter').value,{
 
                success : myDataTable.onDataReturnInitializeTable,
 
                failure : myDataTable.onDataReturnInitializeTable,
 
                scope   : myDataTable,
 
                argument: state
 
        var data = ${c.data|n},
 
            $dataTable = $("#repos_list_wrap").DataTable({
 
                data: data.records,
 
                columns: [
 
                    {data: "menu", sortable: false, className: "quick_repo_menu hidden", searchable: false},
 
                    {data: "raw_name", visible: false, searchable: false},
 
                    {title: "${_('Name')}", data: "name", orderData: 1, render: {
 
                        filter: function(data) {
 
                            return $(data).find(".dt_repo_name").text();
 
                        }
 
                    }},
 
                    {data: "desc", title: "${_('Description')}", searchable: false},
 
                    {data: "last_change_iso", visible: false, searchable: false},
 
                    {data: "last_change", title: "${_('Last Change')}", orderData: 4, searchable: false},
 
                    {data: "last_rev_raw", visible: false, searchable: false},
 
                    {data: "last_changeset", title: "${_('Tip')}", orderData: 6, searchable: false},
 
                    {data: "owner", title: "${_('Owner')}", searchable: false},
 
                    {data: "atom", sortable: false}
 
                ],
 
                order: [[1, "asc"]]
 
            });
 

	
 
        };
 
        $('#q_filter').click(function(){
 
            if(!$('#q_filter').hasClass('loaded')){
 
                //TODO: load here full list later to do search within groups
 
                $('#q_filter').addClass('loaded');
 
            }
 
        });
 

	
 
        $('#q_filter').keyup(function(){
 
            clearTimeout(filterTimeout);
 
            filterTimeout = setTimeout(updateFilter,600);
 
        });
 

	
 
        if($('#q_filter').val()) {
 
            updateFilter();
 
        }
 
      </script>
0 comments (0 inline, 0 general)