Changeset - bd57d1cb9dc3
[Not reviewed]
beta
0 5 0
Marcin Kuzminski - 15 years ago 2010-12-05 15:47:49
marcin@python-works.com
fixes #62, added option to disable statistics for each repository
5 files changed with 33 insertions and 13 deletions:
0 comments (0 inline, 0 general)
rhodecode/controllers/summary.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
"""
 
    package.rhodecode.controllers.summary
 
    ~~~~~~~~~~~~~~
 

	
 
    Summary controller for Rhodecode
 
    :created_on: Apr 18, 2010
 
    :author: marcink
 
    :copyright: (C) 2009-2010 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; version 2
 
# of the License or (at your opinion) any later version of the license.
 
# 
 
# 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, write to the Free Software
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 
# MA  02110-1301, USA.
 

	
 
import calendar
 
import logging
 
from time import mktime
 
from datetime import datetime, timedelta
 

	
 
from vcs.exceptions import ChangesetError
 

	
 
from pylons import tmpl_context as c, request, url
 
from vcs.exceptions import ChangesetError
 
from pylons.i18n.translation import _
 

	
 
from rhodecode.model.scm import ScmModel
 
from rhodecode.model.db import Statistics
 

	
 
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
 
from rhodecode.lib.base import BaseController, render
 
from rhodecode.lib.utils import OrderedDict, EmptyChangeset
 
from rhodecode.model.scm import ScmModel
 
from rhodecode.model.db import Statistics
 
from webhelpers.paginate import Page
 

	
 
from rhodecode.lib.celerylib import run_task
 
from rhodecode.lib.celerylib.tasks import get_commits_stats
 
from datetime import datetime, timedelta
 
from time import mktime
 
import calendar
 
import logging
 

	
 
from webhelpers.paginate import Page
 

	
 
try:
 
    import json
 
except ImportError:
 
    #python 2.5 compatibility
 
    import simplejson as json
 
log = logging.getLogger(__name__)
 

	
 
class SummaryController(BaseController):
 

	
 
    @LoginRequired()
 
    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
 
                                   'repository.admin')
 
    def __before__(self):
 
        super(SummaryController, self).__before__()
 

	
 
    def index(self):
 
        scm_model = ScmModel()
 
        c.repo_info = scm_model.get_repo(c.repo_name)
 
        c.following = scm_model.is_following_repo(c.repo_name,
 
                                             c.rhodecode_user.user_id)
 
        def url_generator(**kw):
 
            return url('shortlog_home', repo_name=c.repo_name, **kw)
 

	
 
        c.repo_changesets = Page(c.repo_info, page=1, items_per_page=10,
 
                                 url=url_generator)
 

	
 
        e = request.environ
 

	
 
        if self.rhodecode_user.username == 'default':
 
            password = ':default'
 
        else:
 
            password = ''
 

	
 
        uri = u'%(protocol)s://%(user)s%(password)s@%(host)s%(prefix)s/%(repo_name)s' % {
 
                                        'protocol': e.get('wsgi.url_scheme'),
 
                                        'user':str(c.rhodecode_user.username),
 
                                        'password':password,
 
                                        'host':e.get('HTTP_HOST'),
 
                                        'prefix':e.get('SCRIPT_NAME'),
 
                                        'repo_name':c.repo_name, }
 
        c.clone_repo_url = uri
 
        c.repo_tags = OrderedDict()
 
        for name, hash in c.repo_info.tags.items()[:10]:
 
            try:
 
                c.repo_tags[name] = c.repo_info.get_changeset(hash)
 
            except ChangesetError:
 
                c.repo_tags[name] = EmptyChangeset(hash)
 

	
 
        c.repo_branches = OrderedDict()
 
        for name, hash in c.repo_info.branches.items()[:10]:
 
            try:
 
                c.repo_branches[name] = c.repo_info.get_changeset(hash)
 
            except ChangesetError:
 
                c.repo_branches[name] = EmptyChangeset(hash)
 

	
 
        td = datetime.today() + timedelta(days=1)
 
        y, m, d = td.year, td.month, td.day
 

	
 
        ts_min_y = mktime((y - 1, (td - timedelta(days=calendar.mdays[m])).month,
 
                            d, 0, 0, 0, 0, 0, 0,))
 
        ts_min_m = mktime((y, (td - timedelta(days=calendar.mdays[m])).month,
 
                            d, 0, 0, 0, 0, 0, 0,))
 

	
 
        ts_max_y = mktime((y, m, d, 0, 0, 0, 0, 0, 0,))
 

	
 
        run_task(get_commits_stats, c.repo_info.name, ts_min_y, ts_max_y)
 
        if c.repo_info.dbrepo.enable_statistics:
 
            c.no_data_msg = _('No data loaded yet')
 
            run_task(get_commits_stats, c.repo_info.name, ts_min_y, ts_max_y)
 
        else:
 
            c.no_data_msg = _('Statistics are disabled for this repository')
 
        c.ts_min = ts_min_m
 
        c.ts_max = ts_max_y
 

	
 
        stats = self.sa.query(Statistics)\
 
            .filter(Statistics.repository == c.repo_info.dbrepo)\
 
            .scalar()
 

	
 

	
 
        if stats and stats.languages:
 
            c.no_data = False
 
            lang_stats = json.loads(stats.languages)
 
            c.commit_data = stats.commit_activity
 
            c.overview_data = stats.commit_activity_combined
 
            c.trending_languages = json.dumps(OrderedDict(
 
                                       sorted(lang_stats.items(), reverse=True,
 
                                            key=lambda k: k[1])[:10]
 
                                        )
 
                                    )
 
        else:
 
            c.commit_data = json.dumps({})
 
            c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 10] ])
 
            c.trending_languages = json.dumps({})
 
            c.no_data = True
 

	
 
        return render('summary/summary.html')
 

	
rhodecode/model/db.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
"""
 
    package.rhodecode.model.db
 
    ~~~~~~~~~~~~~~
 
    
 
    Database Models for RhodeCode    
 
    :created_on: Apr 08, 2010
 
    :author: marcink
 
    :copyright: (C) 2009-2010 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; version 2
 
# of the License or (at your opinion) any later version of the license.
 
# 
 
# 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, write to the Free Software
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 
# MA  02110-1301, USA.
 
import logging
 
import datetime
 

	
 
from sqlalchemy import *
 
from sqlalchemy.exc import DatabaseError
 
from sqlalchemy.orm import relation, backref
 
from sqlalchemy.orm.session import Session
 

	
 
from rhodecode.model.meta import Base
 

	
 
log = logging.getLogger(__name__)
 

	
 
class RhodeCodeSettings(Base):
 
    __tablename__ = 'rhodecode_settings'
 
    __table_args__ = (UniqueConstraint('app_settings_name'), {'useexisting':True})
 
    app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    app_settings_name = Column("app_settings_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    app_settings_value = Column("app_settings_value", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 

	
 
    def __init__(self, k, v):
 
        self.app_settings_name = k
 
        self.app_settings_value = v
 

	
 
    def __repr__(self):
 
        return "<RhodeCodeSetting('%s:%s')>" % (self.app_settings_name,
 
                                                self.app_settings_value)
 

	
 
class RhodeCodeUi(Base):
 
    __tablename__ = 'rhodecode_ui'
 
    __table_args__ = {'useexisting':True}
 
    ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    ui_section = Column("ui_section", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    ui_key = Column("ui_key", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    ui_value = Column("ui_value", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
 

	
 

	
 
class User(Base):
 
    __tablename__ = 'users'
 
    __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'useexisting':True})
 
    user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    username = Column("username", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    password = Column("password", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    active = Column("active", Boolean(), nullable=True, unique=None, default=None)
 
    admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
 
    name = Column("name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    lastname = Column("lastname", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    email = Column("email", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
 
    is_ldap = Column("is_ldap", Boolean(), nullable=False, unique=None, default=False)
 

	
 
    user_log = relation('UserLog', cascade='all')
 
    user_perms = relation('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
 

	
 
    repositories = relation('Repository')
 
    user_followers = relation('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
 

	
 
    @property
 
    def full_contact(self):
 
        return '%s %s <%s>' % (self.name, self.lastname, self.email)
 

	
 
    def __repr__(self):
 
        return "<User('id:%s:%s')>" % (self.user_id, self.username)
 

	
 
    def update_lastlogin(self):
 
        """Update user lastlogin"""
 

	
 
        try:
 
            session = Session.object_session(self)
 
            self.last_login = datetime.datetime.now()
 
            session.add(self)
 
            session.commit()
 
            log.debug('updated user %s lastlogin', self.username)
 
        except (DatabaseError,):
 
            session.rollback()
 

	
 

	
 
class UserLog(Base):
 
    __tablename__ = 'user_logs'
 
    __table_args__ = {'useexisting':True}
 
    user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    user_id = Column("user_id", Integer(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
 
    repository_id = Column("repository_id", Integer(length=None, convert_unicode=False, assert_unicode=None), ForeignKey(u'repositories.repo_id'), nullable=False, unique=None, default=None)
 
    repository_name = Column("repository_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    user_ip = Column("user_ip", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    action = Column("action", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
 

	
 
    user = relation('User')
 
    repository = relation('Repository')
 

	
 
class Repository(Base):
 
    __tablename__ = 'repositories'
 
    __table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
 
    repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    repo_name = Column("repo_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
 
    repo_type = Column("repo_type", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
 
    user_id = Column("user_id", Integer(), ForeignKey(u'users.user_id'), nullable=False, unique=False, default=None)
 
    private = Column("private", Boolean(), nullable=True, unique=None, default=None)
 
    enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
 
    description = Column("description", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    fork_id = Column("fork_id", Integer(), ForeignKey(u'repositories.repo_id'), nullable=True, unique=False, default=None)
 

	
 
    user = relation('User')
 
    fork = relation('Repository', remote_side=repo_id)
 
    repo_to_perm = relation('RepoToPerm', cascade='all')
 
    stats = relation('Statistics', cascade='all', uselist=False)
 

	
 
    repo_followers = relation('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
 

	
 

	
 
    def __repr__(self):
 
        return "<Repository('%s:%s')>" % (self.repo_id, self.repo_name)
 

	
 
class Permission(Base):
 
    __tablename__ = 'permissions'
 
    __table_args__ = {'useexisting':True}
 
    permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    permission_name = Column("permission_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    permission_longname = Column("permission_longname", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 

	
 
    def __repr__(self):
 
        return "<Permission('%s:%s')>" % (self.permission_id, self.permission_name)
 

	
 
class RepoToPerm(Base):
 
    __tablename__ = 'repo_to_perm'
 
    __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'useexisting':True})
 
    repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    user_id = Column("user_id", Integer(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
 
    permission_id = Column("permission_id", Integer(), ForeignKey(u'permissions.permission_id'), nullable=False, unique=None, default=None)
 
    repository_id = Column("repository_id", Integer(), ForeignKey(u'repositories.repo_id'), nullable=False, unique=None, default=None)
 

	
 
    user = relation('User')
 
    permission = relation('Permission')
 
    repository = relation('Repository')
 

	
 
class UserToPerm(Base):
 
    __tablename__ = 'user_to_perm'
 
    __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'useexisting':True})
 
    user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    user_id = Column("user_id", Integer(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
 
    permission_id = Column("permission_id", Integer(), ForeignKey(u'permissions.permission_id'), nullable=False, unique=None, default=None)
 

	
 
    user = relation('User')
 
    permission = relation('Permission')
 

	
 
class Statistics(Base):
 
    __tablename__ = 'statistics'
 
    __table_args__ = (UniqueConstraint('repository_id'), {'useexisting':True})
 
    stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    repository_id = Column("repository_id", Integer(), ForeignKey(u'repositories.repo_id'), nullable=False, unique=True, default=None)
 
    stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
 
    commit_activity = Column("commit_activity", LargeBinary(), nullable=False)#JSON data
 
    commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
 
    languages = Column("languages", LargeBinary(), nullable=False)#JSON data
 

	
 
    repository = relation('Repository', single_parent=True)
 

	
 
class UserFollowing(Base):
 
    __tablename__ = 'user_followings'
 
    __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
 
                      UniqueConstraint('user_id', 'follows_user_id')
 
                      , {'useexisting':True})
 

	
 
    user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    user_id = Column("user_id", Integer(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
 
    follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey(u'repositories.repo_id'), nullable=True, unique=None, default=None)
 
    follows_user_id = Column("follows_user_id", Integer(), ForeignKey(u'users.user_id'), nullable=True, unique=None, default=None)
 

	
 
    user = relation('User', primaryjoin='User.user_id==UserFollowing.user_id')
 

	
 
    follows_user = relation('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
 
    follows_repository = relation('Repository')
 

	
 

	
 
class CacheInvalidation(Base):
 
    __tablename__ = 'cache_invalidation'
 
    __table_args__ = (UniqueConstraint('cache_key'), {'useexisting':True})
 
    cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    cache_key = Column("cache_key", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    cache_args = Column("cache_args", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
 

	
 

	
 
    def __init__(self, cache_key, cache_args=''):
 
        self.cache_key = cache_key
 
        self.cache_args = cache_args
 
        self.cache_active = False
 

	
 
    def __repr__(self):
 
        return "<CacheInvalidation('%s:%s')>" % (self.cache_id, self.cache_key)
rhodecode/model/forms.py
Show inline comments
 
""" this is forms validation classes
 
http://formencode.org/module-formencode.validators.html
 
for list off all availible validators
 

	
 
we can create our own validators
 

	
 
The table below outlines the options which can be used in a schema in addition to the validators themselves
 
pre_validators          []     These validators will be applied before the schema
 
chained_validators      []     These validators will be applied after the schema
 
allow_extra_fields      False     If True, then it is not an error when keys that aren't associated with a validator are present
 
filter_extra_fields     False     If True, then keys that aren't associated with a validator are removed
 
if_key_missing          NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
 
ignore_key_missing      False     If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already    
 
  
 
  
 
<name> = formencode.validators.<name of validator>
 
<name> must equal form name
 
list=[1,2,3,4,5]
 
for SELECT use formencode.All(OneOf(list), Int())
 
    
 
"""
 
import os
 
import re
 
import logging
 

	
 
import formencode
 
from formencode import All
 
from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
 
    Email, Bool, StringBoolean
 

	
 
from pylons.i18n.translation import _
 

	
 
import rhodecode.lib.helpers as h
 
from rhodecode.lib.auth import authenticate, get_crypt_password
 
from rhodecode.lib.exceptions import LdapImportError
 
from rhodecode.model import meta
 
from rhodecode.model.user import UserModel
 
from rhodecode.model.repo import RepoModel
 
from rhodecode.model.db import User
 
from rhodecode import BACKENDS
 

	
 
from webhelpers.pylonslib.secure_form import authentication_token
 

	
 
log = logging.getLogger(__name__)
 

	
 
#this is needed to translate the messages using _() in validators
 
class State_obj(object):
 
    _ = staticmethod(_)
 

	
 
#===============================================================================
 
# VALIDATORS
 
#===============================================================================
 
class ValidAuthToken(formencode.validators.FancyValidator):
 
    messages = {'invalid_token':_('Token mismatch')}
 

	
 
    def validate_python(self, value, state):
 

	
 
        if value != authentication_token():
 
            raise formencode.Invalid(self.message('invalid_token', state,
 
                                            search_number=value), value, state)
 

	
 
def ValidUsername(edit, old_data):
 
    class _ValidUsername(formencode.validators.FancyValidator):
 

	
 
        def validate_python(self, value, state):
 
            if value in ['default', 'new_user']:
 
                raise formencode.Invalid(_('Invalid username'), value, state)
 
            #check if user is unique
 
            old_un = None
 
            if edit:
 
                old_un = UserModel().get(old_data.get('user_id')).username
 

	
 
            if old_un != value or not edit:
 
                if UserModel().get_by_username(value, cache=False,
 
                                               case_insensitive=True):
 
                    raise formencode.Invalid(_('This username already exists') ,
 
                                             value, state)
 

	
 

	
 
            if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_]+$', value) is None:
 
                raise formencode.Invalid(_('Username may only contain '
 
                                           'alphanumeric characters underscores '
 
                                           'or dashes and must begin with '
 
                                           'alphanumeric character'),
 
                                      value, state)
 

	
 

	
 

	
 
    return _ValidUsername
 

	
 
class ValidPassword(formencode.validators.FancyValidator):
 

	
 
    def to_python(self, value, state):
 

	
 
        if value:
 

	
 
            if value.get('password'):
 
                try:
 
                    value['password'] = get_crypt_password(value['password'])
 
                except UnicodeEncodeError:
 
                    e_dict = {'password':_('Invalid characters in password')}
 
                    raise formencode.Invalid('', value, state, error_dict=e_dict)
 

	
 
            if value.get('password_confirmation'):
 
                try:
 
                    value['password_confirmation'] = \
 
                        get_crypt_password(value['password_confirmation'])
 
                except UnicodeEncodeError:
 
                    e_dict = {'password_confirmation':_('Invalid characters in password')}
 
                    raise formencode.Invalid('', value, state, error_dict=e_dict)
 

	
 
            if value.get('new_password'):
 
                try:
 
                    value['new_password'] = \
 
                        get_crypt_password(value['new_password'])
 
                except UnicodeEncodeError:
 
                    e_dict = {'new_password':_('Invalid characters in password')}
 
                    raise formencode.Invalid('', value, state, error_dict=e_dict)
 

	
 
            return value
 

	
 
class ValidPasswordsMatch(formencode.validators.FancyValidator):
 

	
 
    def validate_python(self, value, state):
 

	
 
        if value['password'] != value['password_confirmation']:
 
            e_dict = {'password_confirmation':
 
                   _('Password do not match')}
 
            raise formencode.Invalid('', value, state, error_dict=e_dict)
 

	
 
class ValidAuth(formencode.validators.FancyValidator):
 
    messages = {
 
            'invalid_password':_('invalid password'),
 
            'invalid_login':_('invalid user name'),
 
            'disabled_account':_('Your account is disabled')
 

	
 
            }
 
    #error mapping
 
    e_dict = {'username':messages['invalid_login'],
 
              'password':messages['invalid_password']}
 
    e_dict_disable = {'username':messages['disabled_account']}
 

	
 
    def validate_python(self, value, state):
 
        password = value['password']
 
        username = value['username']
 
        user = UserModel().get_by_username(username)
 

	
 
        if authenticate(username, password):
 
            return value
 
        else:
 
            if user and user.active is False:
 
                log.warning('user %s is disabled', username)
 
                raise formencode.Invalid(self.message('disabled_account',
 
                                         state=State_obj),
 
                                         value, state,
 
                                         error_dict=self.e_dict_disable)
 
            else:
 
                log.warning('user %s not authenticated', username)
 
                raise formencode.Invalid(self.message('invalid_password',
 
                                         state=State_obj), value, state,
 
                                         error_dict=self.e_dict)
 

	
 
class ValidRepoUser(formencode.validators.FancyValidator):
 

	
 
    def to_python(self, value, state):
 
        sa = meta.Session()
 
        try:
 
            self.user_db = sa.query(User)\
 
                .filter(User.active == True)\
 
                .filter(User.username == value).one()
 
        except Exception:
 
            raise formencode.Invalid(_('This username is not valid'),
 
                                     value, state)
 
        finally:
 
            meta.Session.remove()
 

	
 
        return self.user_db.user_id
 

	
 
def ValidRepoName(edit, old_data):
 
    class _ValidRepoName(formencode.validators.FancyValidator):
 

	
 
        def to_python(self, value, state):
 
            slug = h.repo_name_slug(value)
 
            if slug in ['_admin']:
 
                raise formencode.Invalid(_('This repository name is disallowed'),
 
                                         value, state)
 
            if old_data.get('repo_name') != value or not edit:
 
                if RepoModel().get_by_repo_name(slug, cache=False):
 
                    raise formencode.Invalid(_('This repository already exists') ,
 
                                             value, state)
 
            return slug
 

	
 

	
 
    return _ValidRepoName
 

	
 
def ValidForkType(old_data):
 
    class _ValidForkType(formencode.validators.FancyValidator):
 

	
 
        def to_python(self, value, state):
 
            if old_data['repo_type'] != value:
 
                raise formencode.Invalid(_('Fork have to be the same type as original'),
 
                                         value, state)
 
            return value
 
    return _ValidForkType
 

	
 
class ValidPerms(formencode.validators.FancyValidator):
 
    messages = {'perm_new_user_name':_('This username is not valid')}
 

	
 
    def to_python(self, value, state):
 
        perms_update = []
 
        perms_new = []
 
        #build a list of permission to update and new permission to create
 
        for k, v in value.items():
 
            if k.startswith('perm_'):
 
                if  k.startswith('perm_new_user'):
 
                    new_perm = value.get('perm_new_user', False)
 
                    new_user = value.get('perm_new_user_name', False)
 
                    if new_user and new_perm:
 
                        if (new_user, new_perm) not in perms_new:
 
                            perms_new.append((new_user, new_perm))
 
                else:
 
                    usr = k[5:]
 
                    if usr == 'default':
 
                        if value['private']:
 
                            #set none for default when updating to private repo
 
                            v = 'repository.none'
 
                    perms_update.append((usr, v))
 
        value['perms_updates'] = perms_update
 
        value['perms_new'] = perms_new
 
        sa = meta.Session
 
        for k, v in perms_new:
 
            try:
 
                self.user_db = sa.query(User)\
 
                    .filter(User.active == True)\
 
                    .filter(User.username == k).one()
 
            except Exception:
 
                msg = self.message('perm_new_user_name',
 
                                     state=State_obj)
 
                raise formencode.Invalid(msg, value, state,
 
                                         error_dict={'perm_new_user_name':msg})
 
        return value
 

	
 
class ValidSettings(formencode.validators.FancyValidator):
 

	
 
    def to_python(self, value, state):
 
        #settings  form can't edit user
 
        if value.has_key('user'):
 
            del['value']['user']
 

	
 
        return value
 

	
 
class ValidPath(formencode.validators.FancyValidator):
 
    def to_python(self, value, state):
 

	
 
        if not os.path.isdir(value):
 
            msg = _('This is not a valid path')
 
            raise formencode.Invalid(msg, value, state,
 
                                     error_dict={'paths_root_path':msg})
 
        return value
 

	
 
def UniqSystemEmail(old_data):
 
    class _UniqSystemEmail(formencode.validators.FancyValidator):
 
        def to_python(self, value, state):
 
            value = value.lower()
 
            if old_data.get('email') != value:
 
                sa = meta.Session()
 
                try:
 
                    user = sa.query(User).filter(User.email == value).scalar()
 
                    if user:
 
                        raise formencode.Invalid(_("This e-mail address is already taken") ,
 
                                                 value, state)
 
                finally:
 
                    meta.Session.remove()
 

	
 
            return value
 

	
 
    return _UniqSystemEmail
 

	
 
class ValidSystemEmail(formencode.validators.FancyValidator):
 
    def to_python(self, value, state):
 
        value = value.lower()
 
        sa = meta.Session
 
        try:
 
            user = sa.query(User).filter(User.email == value).scalar()
 
            if  user is None:
 
                raise formencode.Invalid(_("This e-mail address doesn't exist.") ,
 
                                         value, state)
 
        finally:
 
            meta.Session.remove()
 

	
 
        return value
 

	
 
class LdapLibValidator(formencode.validators.FancyValidator):
 

	
 
    def to_python(self, value, state):
 

	
 
        try:
 
            import ldap
 
        except ImportError:
 
            raise LdapImportError
 
        return value
 

	
 
class BaseDnValidator(formencode.validators.FancyValidator):
 

	
 
    def to_python(self, value, state):
 

	
 
        try:
 
            value % {'user':'valid'}
 

	
 
            if value.find('%(user)s') == -1:
 
                raise formencode.Invalid(_("You need to specify %(user)s in "
 
                                           "template for example uid=%(user)s "
 
                                           ",dc=company...") ,
 
                                         value, state)
 

	
 
        except KeyError:
 
            raise formencode.Invalid(_("Wrong template used, only %(user)s "
 
                                       "is an valid entry") ,
 
                                         value, state)
 

	
 
        return value
 

	
 
#===============================================================================
 
# FORMS        
 
#===============================================================================
 
class LoginForm(formencode.Schema):
 
    allow_extra_fields = True
 
    filter_extra_fields = True
 
    username = UnicodeString(
 
                             strip=True,
 
                             min=1,
 
                             not_empty=True,
 
                             messages={
 
                                       'empty':_('Please enter a login'),
 
                                       'tooShort':_('Enter a value %(min)i characters long or more')}
 
                            )
 

	
 
    password = UnicodeString(
 
                            strip=True,
 
                            min=6,
 
                            not_empty=True,
 
                            messages={
 
                                      'empty':_('Please enter a password'),
 
                                      'tooShort':_('Enter %(min)i characters or more')}
 
                                )
 

	
 

	
 
    #chained validators have access to all data
 
    chained_validators = [ValidAuth]
 

	
 
def UserForm(edit=False, old_data={}):
 
    class _UserForm(formencode.Schema):
 
        allow_extra_fields = True
 
        filter_extra_fields = True
 
        username = All(UnicodeString(strip=True, min=1, not_empty=True),
 
                       ValidUsername(edit, old_data))
 
        if edit:
 
            new_password = All(UnicodeString(strip=True, min=6, not_empty=False))
 
            admin = StringBoolean(if_missing=False)
 
        else:
 
            password = All(UnicodeString(strip=True, min=6, not_empty=True))
 
        active = StringBoolean(if_missing=False)
 
        name = UnicodeString(strip=True, min=1, not_empty=True)
 
        lastname = UnicodeString(strip=True, min=1, not_empty=True)
 
        email = All(Email(not_empty=True), UniqSystemEmail(old_data))
 

	
 
        chained_validators = [ValidPassword]
 

	
 
    return _UserForm
 

	
 
def RegisterForm(edit=False, old_data={}):
 
    class _RegisterForm(formencode.Schema):
 
        allow_extra_fields = True
 
        filter_extra_fields = True
 
        username = All(ValidUsername(edit, old_data),
 
                       UnicodeString(strip=True, min=1, not_empty=True))
 
        password = All(UnicodeString(strip=True, min=6, not_empty=True))
 
        password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=True))
 
        active = StringBoolean(if_missing=False)
 
        name = UnicodeString(strip=True, min=1, not_empty=True)
 
        lastname = UnicodeString(strip=True, min=1, not_empty=True)
 
        email = All(Email(not_empty=True), UniqSystemEmail(old_data))
 

	
 
        chained_validators = [ValidPasswordsMatch, ValidPassword]
 

	
 
    return _RegisterForm
 

	
 
def PasswordResetForm():
 
    class _PasswordResetForm(formencode.Schema):
 
        allow_extra_fields = True
 
        filter_extra_fields = True
 
        email = All(ValidSystemEmail(), Email(not_empty=True))
 
    return _PasswordResetForm
 

	
 
def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
 
    class _RepoForm(formencode.Schema):
 
        allow_extra_fields = True
 
        filter_extra_fields = False
 
        repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
 
                        ValidRepoName(edit, old_data))
 
        description = UnicodeString(strip=True, min=1, not_empty=True)
 
        private = StringBoolean(if_missing=False)
 
        enable_statistics = StringBoolean(if_missing=False)
 
        repo_type = OneOf(supported_backends)
 
        if edit:
 
            user = All(Int(not_empty=True), ValidRepoUser)
 

	
 
        chained_validators = [ValidPerms]
 
    return _RepoForm
 

	
 
def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
 
    class _RepoForkForm(formencode.Schema):
 
        allow_extra_fields = True
 
        filter_extra_fields = False
 
        fork_name = All(UnicodeString(strip=True, min=1, not_empty=True),
 
                        ValidRepoName(edit, old_data))
 
        description = UnicodeString(strip=True, min=1, not_empty=True)
 
        private = StringBoolean(if_missing=False)
 
        repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
 
    return _RepoForkForm
 

	
 
def RepoSettingsForm(edit=False, old_data={}):
 
    class _RepoForm(formencode.Schema):
 
        allow_extra_fields = True
 
        filter_extra_fields = False
 
        repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
 
                        ValidRepoName(edit, old_data))
 
        description = UnicodeString(strip=True, min=1, not_empty=True)
 
        private = StringBoolean(if_missing=False)
 

	
 
        chained_validators = [ValidPerms, ValidSettings]
 
    return _RepoForm
 

	
 

	
 
def ApplicationSettingsForm():
 
    class _ApplicationSettingsForm(formencode.Schema):
 
        allow_extra_fields = True
 
        filter_extra_fields = False
 
        rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
 
        rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
 

	
 
    return _ApplicationSettingsForm
 

	
 
def ApplicationUiSettingsForm():
 
    class _ApplicationUiSettingsForm(formencode.Schema):
 
        allow_extra_fields = True
 
        filter_extra_fields = False
 
        web_push_ssl = OneOf(['true', 'false'], if_missing='false')
 
        paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
 
        hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
 
        hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
 
        hooks_pretxnchangegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
 
        hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
 

	
 
    return _ApplicationUiSettingsForm
 

	
 
def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
 
    class _DefaultPermissionsForm(formencode.Schema):
 
        allow_extra_fields = True
 
        filter_extra_fields = True
 
        overwrite_default = StringBoolean(if_missing=False)
 
        anonymous = OneOf(['True', 'False'], if_missing=False)
 
        default_perm = OneOf(perms_choices)
 
        default_register = OneOf(register_choices)
 
        default_create = OneOf(create_choices)
 

	
 
    return _DefaultPermissionsForm
 

	
 

	
 
def LdapSettingsForm():
 
    class _LdapSettingsForm(formencode.Schema):
 
        allow_extra_fields = True
 
        filter_extra_fields = True
 
        pre_validators = [LdapLibValidator]
 
        ldap_active = StringBoolean(if_missing=False)
 
        ldap_host = UnicodeString(strip=True,)
 
        ldap_port = Number(strip=True,)
 
        ldap_ldaps = StringBoolean(if_missing=False)
 
        ldap_dn_user = UnicodeString(strip=True,)
 
        ldap_dn_pass = UnicodeString(strip=True,)
 
        ldap_base_dn = All(BaseDnValidator, UnicodeString(strip=True,))
 

	
 
    return _LdapSettingsForm
rhodecode/templates/admin/repos/repo_edit.html
Show inline comments
 
## -*- coding: utf-8 -*-
 
<%inherit file="/base/base.html"/>
 

	
 
<%def name="title()">
 
    ${_('Edit repository')} ${c.repo_info.repo_name} - ${c.rhodecode_name}
 
</%def>
 

	
 
<%def name="breadcrumbs_links()">
 
    ${h.link_to(_('Admin'),h.url('admin_home'))} 
 
    &raquo; 
 
    ${h.link_to(_('Repositories'),h.url('repos'))} 
 
    &raquo;
 
    ${_('edit')} "${c.repo_name}"
 
</%def>
 

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

	
 
<%def name="main()">
 
<div class="box box-left">
 
    <!-- box / title -->
 
    <div class="title">
 
        ${self.breadcrumbs()}      
 
    </div>
 
    ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='put')}
 
    <div class="form">
 
        <!-- fields -->
 
        <div class="fields">
 
            <div class="field">
 
                <div class="label">
 
                    <label for="repo_name">${_('Name')}:</label>
 
                </div>
 
                <div class="input">
 
                    ${h.text('repo_name',class_="medium")}
 
                </div>
 
             </div>
 
            <div class="field">
 
                <div class="label">
 
                    <label for="repo_type">${_('Type')}:</label>
 
                </div>
 
                <div class="input">
 
                    ${h.select('repo_type','hg',c.backends,class_="medium")}
 
                </div>
 
             </div>             
 
            <div class="field">
 
                <div class="label label-textarea">
 
                    <label for="description">${_('Description')}:</label>
 
                </div>
 
                <div class="textarea text-area editor">
 
                    ${h.textarea('description',cols=23,rows=5)}
 
                </div>
 
            </div>
 
            
 
            <div class="field">
 
                <div class="label label-checkbox">
 
                    <label for="private">${_('Private')}:</label>
 
                </div>
 
                <div class="checkboxes">
 
                    ${h.checkbox('private',value="True")}
 
                </div>
 
            </div>
 
             
 
            <div class="field">
 
                <div class="label label-checkbox">
 
                    <label for="enable_statistics">${_('Enable statistics')}:</label>
 
                </div>
 
                <div class="checkboxes">
 
                    ${h.checkbox('enable_statistics',value="True")}
 
                </div>
 
            </div>             
 
            <div class="field">
 
                <div class="label">
 
                    <label for="user">${_('Owner')}:</label>
 
                </div>
 
                <div class="input input-small ac">
 
                    <div class="perm_ac">
 
                       ${h.text('user',class_='yui-ac-input')}
 
                       <div id="owner_container"></div>
 
                    </div>
 
                </div>
 
             </div>                
 
             
 
            <div class="field">
 
                <div class="label">
 
                    <label for="input">${_('Permissions')}:</label>
 
                </div>
 
                <div class="input">
 
                    <table id="permissions_manage">
 
                        <tr>
 
                            <td>${_('none')}</td>
 
                            <td>${_('read')}</td>
 
                            <td>${_('write')}</td>
 
                            <td>${_('admin')}</td>
 
                            <td>${_('user')}</td>
 
                            <td></td>
 
                        </tr>
 
                        
 
                        %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">${r2p.user.username}</td>
 
                                </tr>
 
                            %else:
 
                            <tr id="id${id(r2p.user.username)}">
 
                                <td>${h.radio('perm_%s' % r2p.user.username,'repository.none')}</td>
 
                                <td>${h.radio('perm_%s' % r2p.user.username,'repository.read')}</td>
 
                                <td>${h.radio('perm_%s' % r2p.user.username,'repository.write')}</td>
 
                                <td>${h.radio('perm_%s' % r2p.user.username,'repository.admin')}</td>
 
                                <td>${r2p.user.username}</td>
 
                                <td>
 
                                  %if r2p.user.username !='default':
 
                                    <span class="delete_icon action_button" onclick="ajaxAction(${r2p.user.user_id},'${'id%s'%id(r2p.user.username)}')">
 
                                        <script type="text/javascript">
 
                                            function ajaxAction(user_id,field_id){
 
                                                var sUrl = "${h.url('delete_repo_user',repo_name=c.repo_name)}";
 
                                                var callback = { success:function(o){
 
                                                var tr = YAHOO.util.Dom.get(String(field_id));
 
                                                tr.parentNode.removeChild(tr);},failure:function(o){
 
                                                    alert("${_('Failed to remove user')}");},};
 
                                                var postData = '_method=delete&user_id='+user_id; 
 
                                                var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);};
 
                                        </script>           
 
                                    </span>
 
                                  %endif                    
 
                                </td>
 
                            </tr>
 
                            %endif
 
                        %endfor
 

	
 
                        <tr id="add_perm_input">
 
                            <td>${h.radio('perm_new_user','repository.none')}</td>
 
                            <td>${h.radio('perm_new_user','repository.read')}</td>
 
                            <td>${h.radio('perm_new_user','repository.write')}</td>
 
                            <td>${h.radio('perm_new_user','repository.admin')}</td>
 
                            <td class='ac'>
 
                                <div class="perm_ac" id="perm_ac">
 
                                    ${h.text('perm_new_user_name',class_='yui-ac-input')}
 
                                    <div id="perm_container"></div>
 
                                </div>
 
                            </td>
 
                            <td></td>
 
                        </tr>
 
                        <tr>
 
                            <td colspan="6">
 
                                <span id="add_perm" class="add_icon" style="cursor: pointer;">
 
                                ${_('Add another user')}
 
                                </span>
 
                            </td>
 
                        </tr>
 
                    </table>             
 
             </div>
 
             
 
            <div class="buttons">
 
              ${h.submit('save','Save',class_="ui-button ui-widget ui-state-default ui-corner-all")}
 
              ${h.reset('reset','Reset',class_="ui-button ui-widget ui-state-default ui-corner-all")}
 
            </div>                                                          
 
        </div>
 
    </div>
 
    </div>    
 
    ${h.end_form()}
 
        <script type="text/javascript">
 
            YAHOO.util.Event.onDOMReady(function(){
 
                var D = YAHOO.util.Dom;
 
                if(!D.hasClass('perm_new_user_name','error')){
 
                    D.setStyle('add_perm_input','display','none');
 
                }
 
                YAHOO.util.Event.addListener('add_perm','click',function(){
 
                    D.setStyle('add_perm_input','display','');
 
                    D.setStyle('add_perm','opacity','0.6');
 
                    D.setStyle('add_perm','cursor','default');
 
                });
 
            });
 
        </script>
 
        <script type="text/javascript">    
 
        YAHOO.example.FnMultipleFields = function(){
 
            var myContacts = ${c.users_array|n}
 
            
 
            // Define a custom search function for the DataSource
 
            var matchNames = function(sQuery) {
 
                // Case insensitive matching
 
                var query = sQuery.toLowerCase(),
 
                    contact,
 
                    i=0,
 
                    l=myContacts.length,
 
                    matches = [];
 
                
 
                // Match against each name of each contact
 
                for(; i<l; i++) {
 
                    contact = myContacts[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;
 
            };
 
        
 
            // Use a FunctionDataSource
 
            var oDS = new YAHOO.util.FunctionDataSource(matchNames);
 
            oDS.responseSchema = {
 
                fields: ["id", "fname", "lname", "nname"]
 
            }
 
        
 
            // Instantiate AutoComplete for perms
 
            var oAC_perms = new YAHOO.widget.AutoComplete("perm_new_user_name", "perm_container", oDS);
 
            oAC_perms.useShadow = false;
 
            oAC_perms.resultTypeList = false;
 
            
 
            // Instantiate AutoComplete for owner
 
            var oAC_owner = new YAHOO.widget.AutoComplete("user", "owner_container", oDS);
 
            oAC_owner.useShadow = false;
 
            oAC_owner.resultTypeList = false;
 
            
 
            
 
            // Custom formatter to highlight the matching letters
 
            var custom_formatter = function(oResultData, sQuery, sResultMatch) {
 
                var query = sQuery.toLowerCase(),
 
                    fname = oResultData.fname,
 
                    lname = oResultData.lname,
 
                    nname = oResultData.nname || "", // Guard against null value
 
                    query = sQuery.toLowerCase(),
 
                    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 displayfname + " " + displaylname + " " + displaynname;
 
                
 
            };
 
            oAC_perms.formatResult = custom_formatter; 
 
            oAC_owner.formatResult = custom_formatter;
 
                            
 
            // Helper 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);
 
            };
 
        
 
            var myHandler = 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;
 
            };
 

	
 
            oAC_perms.itemSelectEvent.subscribe(myHandler);
 
            oAC_owner.itemSelectEvent.subscribe(myHandler);
 
            
 
            return {
 
                oDS: oDS,
 
                oAC_perms: oAC_perms,
 
                oAC_owner: oAC_owner, 
 
            };
 
        }();
 
            
 
        </script>      
 

	
 
</div>
 

	
 
<div class="box box-right">
 
    <div class="title">
 
        <h5>${_('Administration')}</h5>    
 
    </div>
 
    
 
        <h3>${_('Statistics')}</h3>
 
        
 
        ${h.form(url('repo_stats', repo_name=c.repo_info.repo_name),method='delete')}
 
        <div class="form">
 
           <div class="fields">
 
               ${h.submit('reset_stats_%s' % c.repo_info.repo_name,_('Reset current statistics'),class_="refresh_icon action_button",onclick="return confirm('Confirm to remove current statistics');")}
 
               
 
               <div class="field">
 
               <ul>
 
                    <li>${_('Fetched to rev')}: ${c.stats_revision}/${c.repo_last_rev}</li>
 
                    <li>${_('Percentage of stats gathered')}: ${c.stats_percentage} %</li>
 
               </ul>
 
               </div>
 
               
 
           </div>
 
        </div>                    
 
        ${h.end_form()}
 
        
 
        <h3>${_('Cache')}</h3>
 
        ${h.form(url('repo_cache', repo_name=c.repo_info.repo_name),method='delete')}
 
        <div class="form">
 
           <div class="fields">
 
               ${h.submit('reset_cache_%s' % c.repo_info.repo_name,_('Invalidate repository cache'),class_="refresh_icon action_button",onclick="return confirm('Confirm to invalidate repository cache');")}
 
           </div>
 
        </div>                    
 
        ${h.end_form()}
 
        
 
        
 
        <h3>${_('Delete')}</h3>
 
        ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='delete')}
 
        <div class="form">
 
           <div class="fields">
 
               ${h.submit('remove_%s' % c.repo_info.repo_name,_('Remove this repository'),class_="delete_icon action_button",onclick="return confirm('Confirm to delete this repository');")}
 
           </div>
 
        </div>                    
 
        ${h.end_form()}
 
    
 
</div>
 

	
 

	
 
</%def> 
 
\ No newline at end of file
rhodecode/templates/summary/summary.html
Show inline comments
 
<%inherit file="/base/base.html"/>
 

	
 
<%def name="title()">
 
    ${c.repo_name} ${_('Summary')} - ${c.rhodecode_name}
 
</%def>
 

	
 
<%def name="breadcrumbs_links()">
 
    ${h.link_to(u'Home',h.url('/'))}
 
    &raquo; 
 
    ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
 
    &raquo;
 
    ${_('summary')}
 
</%def>
 

	
 
<%def name="page_nav()">
 
	${self.menu('summary')}    
 
</%def>
 

	
 
<%def name="main()">
 
<div class="box box-left">
 
    <!-- box / title -->
 
    <div class="title">
 
        ${self.breadcrumbs()}
 
    </div>
 
    <!-- end box / title -->
 
	<div class="form">
 
	  <div class="fields">
 
		 
 
			 <div class="field">
 
			  <div class="label">
 
			      <label>${_('Name')}:</label>
 
			  </div>
 
			  <div class="input-short">
 
		         %if c.repo_info.dbrepo.repo_type =='hg':
 
		           <img style="margin-bottom:2px" class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="/images/icons/hgicon.png"/>
 
		         %endif
 
		         %if c.repo_info.dbrepo.repo_type =='git':
 
		           <img style="margin-bottom:2px" class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="/images/icons/giticon.png"/>
 
		         %endif 
 
                                 			  
 
	             %if c.repo_info.dbrepo.private:
 
	                <img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="/images/icons/lock.png"/>
 
	             %else:
 
	                <img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="/images/icons/lock_open.png"/>
 
	             %endif
 
			      <span style="font-size: 1.6em;font-weight: bold;vertical-align: baseline;">${c.repo_info.name}</span>
 
			      %if c.rhodecode_user.username != 'default':
 
				      %if c.following:
 
	                  <span id="follow_toggle" class="following" title="${_('Stop following this repository')}"
 
	                        onclick="javascript:toggleFollowingRepo(${c.repo_info.dbrepo.repo_id},'${str(h.get_token())}')">
 
	                  </span>			      
 
				      %else:
 
				      <span id="follow_toggle" class="follow" title="${_('Start following this repository')}"
 
				            onclick="javascript:toggleFollowingRepo(${c.repo_info.dbrepo.repo_id},'${str(h.get_token())}')">
 
				      </span>
 
				      %endif
 
				  %endif:
 
			      <br/>
 
		            %if c.repo_info.dbrepo.fork:
 
		            	<span style="margin-top:5px">
 
		            	<a href="${h.url('summary_home',repo_name=c.repo_info.dbrepo.fork.repo_name)}">
 
		            	<img class="icon" alt="${_('public')}"
 
		            	title="${_('Fork of')} ${c.repo_info.dbrepo.fork.repo_name}" 
 
		            	src="/images/icons/arrow_divide.png"/>
 
		            	${_('Fork of')} ${c.repo_info.dbrepo.fork.repo_name}
 
		            	</a>
 
		            	</span>
 
		            %endif			      
 
			  </div>
 
			 </div>
 
			
 
			
 
			 <div class="field">
 
			  <div class="label">
 
			      <label>${_('Description')}:</label>
 
			  </div>
 
			  <div class="input-short">
 
			      ${c.repo_info.dbrepo.description}
 
			  </div>
 
			 </div>
 
			
 
			
 
			 <div class="field">
 
			  <div class="label">
 
			      <label>${_('Contact')}:</label>
 
			  </div>
 
			  <div class="input-short">
 
			  	<div class="gravatar">
 
			  		<img alt="gravatar" src="${h.gravatar_url(c.repo_info.dbrepo.user.email)}"/>
 
			  	</div>
 
			  		${_('Username')}: ${c.repo_info.dbrepo.user.username}<br/>
 
			  		${_('Name')}: ${c.repo_info.dbrepo.user.name} ${c.repo_info.dbrepo.user.lastname}<br/>
 
			  		${_('Email')}: <a href="mailto:${c.repo_info.dbrepo.user.email}">${c.repo_info.dbrepo.user.email}</a>
 
			  </div>
 
			 </div>
 
			
 
			 <div class="field">
 
			  <div class="label">
 
			      <label>${_('Last change')}:</label>
 
			  </div>
 
			  <div class="input-short">
 
			      ${h.age(c.repo_info.last_change)} - ${c.repo_info.last_change} 
 
			      ${_('by')} ${h.get_changeset_safe(c.repo_info,'tip').author} 
 
			      
 
			  </div>
 
			 </div>
 
			
 
			 <div class="field">
 
			  <div class="label">
 
			      <label>${_('Clone url')}:</label>
 
			  </div>
 
			  <div class="input-short">
 
			      <input type="text" id="clone_url" readonly="readonly" value="hg clone ${c.clone_repo_url}" size="70"/>
 
			  </div>
 
			 </div>
 
			 
 
			 <div class="field">
 
			  <div class="label">
 
			      <label>${_('Trending source files')}:</label>
 
			  </div>
 
			  <div class="input-short">
 
			    <div id="lang_stats">
 
			    
 
			    </div> 			  
 
			  	<script type="text/javascript">
 
				  	YUE.onDOMReady(function(e){
 
				  	    id = 'clone_url';
 
				  	    YUE.on(id,'click',function(e){
 
				  	        YUD.get('clone_url').select();
 
				  	    })
 
				  	})
 
			  		var data = ${c.trending_languages|n};
 
			  		var total = 0;
 
			  		var no_data = true;
 
			  		for (k in data){
 
			  		    total += data[k];
 
			  		    no_data = false;
 
			  		} 
 
					var tbl = document.createElement('table');
 
					tbl.setAttribute('class','trending_language_tbl');
 
					var cnt =0;
 
			  		for (k in data){
 
			  			cnt+=1;
 
			  			var hide = cnt>2;
 
				  		var tr = document.createElement('tr');
 
				  		if (hide){
 
				  			tr.setAttribute('style','display:none');
 
				  			tr.setAttribute('class','stats_hidden');
 
				  		}
 
				  		var percentage = Math.round((data[k]/total*100),2);
 
						var value = data[k];
 
				  		var td1 = document.createElement('td');
 
				  		td1.width=150;
 
				  		var trending_language_label = document.createElement('div');
 
				  		trending_language_label.innerHTML = k;
 
				  		td1.appendChild(trending_language_label);
 

	
 
				  		var td2 = document.createElement('td');
 
				  		td2.setAttribute('style','padding-right:14px !important');
 
			  		    var trending_language = document.createElement('div');
 
			  		    var nr_files = value+" ${_('files')}";
 
			  		    
 
			  		    trending_language.title = k+" "+nr_files;
 
			  		    
 
			  		    if (percentage>20){
 
			  		    	trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"% "+nr_files+ "</b>";	
 
			  		    }
 
			  		    else{
 
			  		    	trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"%</b>";
 
			  		    }
 
			  		    
 
			  		    trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
 
			  		    trending_language.style.width=percentage+"%";
 
						td2.appendChild(trending_language);
 
						
 
						tr.appendChild(td1);
 
						tr.appendChild(td2);
 
			  		    tbl.appendChild(tr);
 
			  		    if(cnt == 2){
 
			  		    	var show_more = document.createElement('tr');
 
			  		    	var td=document.createElement('td');
 
			  		    	lnk = document.createElement('a');
 
			  		    	lnk.href='#';
 
			  		    	lnk.innerHTML = "${_("show more")}";
 
			  		    	lnk.id='code_stats_show_more';
 
			  		        td.appendChild(lnk);
 
			  		    	show_more.appendChild(td);
 
			  		    	show_more.appendChild(document.createElement('td'));
 
			  		    	tbl.appendChild(show_more);
 
			  		    }
 
			  		    
 
			  		}
 
			  		if(no_data){
 
			  			var tr = document.createElement('tr');
 
			  			var td1 = document.createElement('td');
 
			  			td1.innerHTML = "${_('No data loaded yet')}";
 
			  			td1.innerHTML = "${c.no_data_msg}";
 
			  			tr.appendChild(td1);
 
			  			tbl.appendChild(tr);
 
					}
 
			  		YUD.get('lang_stats').appendChild(tbl);
 
			  		YUE.on('code_stats_show_more','click',function(){
 
			  			l = YUD.getElementsByClassName('stats_hidden')
 
			  			for (e in l){
 
			  			    YUD.setStyle(l[e],'display','');
 
			  			};
 
			  			YUD.setStyle(YUD.get('code_stats_show_more'),
 
			  					'display','none');
 
			  		})
 
			  		
 
			  	</script>
 
 
 
			  </div>
 
			 </div>
 
			 			
 
			 <div class="field">
 
			  <div class="label">
 
			      <label>${_('Download')}:</label>
 
			  </div>
 
			  <div class="input-short">
 
		        %for cnt,archive in enumerate(c.repo_info._get_archives()):
 
		             %if cnt >=1:
 
		             |
 
		             %endif
 
		             ${h.link_to(c.repo_info.name+'.'+archive['type'],
 
		                h.url('files_archive_home',repo_name=c.repo_info.name,
 
		                revision='tip',fileformat=archive['extension']),class_="archive_icon")}
 
		        %endfor
 
			  </div>
 
			 </div>
 
			 
 
			 <div class="field">
 
			  <div class="label">
 
			      <label>${_('Feeds')}:</label>
 
			  </div>
 
			  <div class="input-short">
 
	            ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.repo_info.name),class_='rss_icon')}
 
	            ${h.link_to(_('Atom'),h.url('atom_feed_home',repo_name=c.repo_info.name),class_='atom_icon')}
 
			  </div>
 
			 </div>				 			 			 
 
	  </div>		 
 
	</div>				
 
</div>
 
        
 
<div class="box box-right"  style="min-height:455px">
 
    <!-- box / title -->
 
    <div class="title">
 
        <h5>${_('Commit activity by day / author')}</h5>
 
    </div>
 
    
 
    <div class="table">
 
        
 
         %if c.no_data:
 
           <div style="padding:0 10px 10px 15px;font-size: 1.2em;">${_('No data loaded yet')}</div>
 
           <div style="padding:0 10px 10px 15px;font-size: 1.2em;">${c.no_data_msg}</div>
 
        %endif:  
 
        <div id="commit_history" style="width:460px;height:300px;float:left"></div>
 
        <div style="clear: both;height: 10px"></div>
 
        <div id="overview" style="width:460px;height:100px;float:left"></div>
 
        
 
    	<div id="legend_data" style="clear:both;margin-top:10px;">
 
	    	<div id="legend_container"></div>
 
	    	<div id="legend_choices">
 
				<table id="legend_choices_tables" style="font-size:smaller;color:#545454"></table>
 
	    	</div>
 
    	</div>
 
		<script type="text/javascript">
 
		/**
 
		 * Plots summary graph
 
		 *
 
		 * @class SummaryPlot
 
		 * @param {from} initial from for detailed graph
 
		 * @param {to} initial to for detailed graph
 
		 * @param {dataset}
 
		 * @param {overview_dataset}
 
		 */
 
		function SummaryPlot(from,to,dataset,overview_dataset) {
 
			var initial_ranges = {
 
			    "xaxis":{
 
				    "from":from,
 
				   	"to":to,
 
				},
 
			};
 
		    var dataset = dataset;
 
		    var overview_dataset = [overview_dataset];
 
		    var choiceContainer = YUD.get("legend_choices");
 
		    var choiceContainerTable = YUD.get("legend_choices_tables");
 
		    var plotContainer = YUD.get('commit_history');
 
		    var overviewContainer = YUD.get('overview');
 
		    
 
		    var plot_options = {
 
				bars: {show:true,align:'center',lineWidth:4},
 
				legend: {show:true, container:"legend_container"},
 
				points: {show:true,radius:0,fill:false},
 
				yaxis: {tickDecimals:0,},
 
				xaxis: {
 
					mode: "time", 
 
					timeformat: "%d/%m",
 
				    min:from,
 
				    max:to,	
 
				}, 
 
				grid: {
 
					hoverable: true, 
 
				    clickable: true,
 
				    autoHighlight:true,
 
				    color: "#999"
 
				},
 
				//selection: {mode: "x"}
 
		    };
 
		    var overview_options = {
 
				legend:{show:false},
 
			    bars: {show:true,barWidth: 2,},
 
			    shadowSize: 0,
 
			    xaxis: {mode: "time", timeformat: "%d/%m/%y",},
 
			    yaxis: {ticks: 3, min: 0,tickDecimals:0,},
 
			    grid: {color: "#999",},
 
			    selection: {mode: "x"}
 
			};
 

	
 
			/**
 
			*get dummy data needed in few places
 
			*/
 
		    function getDummyData(label){
 
		    	return {"label":label,
 
               	 "data":[{"time":0,
 
               		 "commits":0,
 
	                     "added":0,
 
	                     "changed":0,
 
	                     "removed":0,
 
                    }],
 
                    "schema":["commits"],
 
                    "color":'#ffffff',
 
           		}
 
			}
 
			
 
		    /**
 
		     * generate checkboxes accordindly to data
 
		     * @param keys
 
		     * @returns
 
		     */
 
		    function generateCheckboxes(data) {
 
			    //append checkboxes
 
			    var i = 0;
 
			    choiceContainerTable.innerHTML = '';
 
			    for(var pos in data) {
 
			    	
 
			    	data[pos].color = i;
 
			        i++;
 
			        if(data[pos].label != ''){
 
				        choiceContainerTable.innerHTML += '<tr><td>'+
 
				        '<input type="checkbox" name="' + data[pos].label +'" checked="checked" />'
 
				        +data[pos].label+
 
				        '</td></tr>';
 
			        }
 
			    }	
 
		    }
 
		    
 
		    /**
 
		     * ToolTip show
 
		     */
 
		    function showTooltip(x, y, contents) {
 
		        var div=document.getElementById('tooltip');
 
		        if(!div) {
 
		            div = document.createElement('div');
 
		            div.id="tooltip";
 
		            div.style.position="absolute";
 
		            div.style.border='1px solid #fdd';
 
		            div.style.padding='2px';
 
		            div.style.backgroundColor='#fee';
 
		            document.body.appendChild(div);
 
		        }
 
		        YUD.setStyle(div, 'opacity', 0);
 
		        div.innerHTML = contents;
 
		        div.style.top=(y + 5) + "px";
 
		        div.style.left=(x + 5) + "px";
 

	
 
		        var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
 
		        anim.animate();
 
		    }
 
		    
 
			/**
 
			 * This function will detect if selected period has some changesets 
 
			   for this user if it does this data is then pushed for displaying
 
			   Additionally it will only display users that are selected by the checkbox
 
			*/
 
		    function getDataAccordingToRanges(ranges) {
 
		    	
 
		        var data = [];
 
		        var keys = [];
 
				for(var key in dataset){
 
					var push = false;
 
					
 
					//method1 slow !!
 
		            //*
 
		            for(var ds in dataset[key].data){
 
			            commit_data = dataset[key].data[ds];
 
			            if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
 
			            	push = true;
 
			            	break;
 
					    }
 
				    }
 
					//*/
 
				    
 
				    /*//method2 sorted commit data !!!
 
				    
 
				    var first_commit = dataset[key].data[0].time;
 
				    var last_commit = dataset[key].data[dataset[key].data.length-1].time;
 
				    
 
				    if (first_commit >= ranges.xaxis.from && last_commit <= ranges.xaxis.to){
 
						push = true;
 
					}
 
				    //*/
 
				    
 
				    if(push){			
 
				    	data.push(dataset[key]);
 
				    }
 
				}
 
				if(data.length >= 1){
 
					return data;
 
				} 
 
				else{
 
					//just return dummy data for graph to plot itself
 
					return [getDummyData('')];	
 
				}
 
				
 
		    }
 
		    
 
			/**
 
			* redraw using new checkbox data
 
			*/
 
		    function plotchoiced(e,args){
 
			    var cur_data = args[0];
 
			    var cur_ranges = args[1];
 
		    	
 
				var new_data = [];
 
		    	var inputs = choiceContainer.getElementsByTagName("input");
 

	
 
		    	//show only checked labels
 
		        for(var i=0; i<inputs.length; i++) {
 
		            var checkbox_key = inputs[i].name;
 
		            
 
	                if(inputs[i].checked){
 
						for(var d in cur_data){
 
							if(cur_data[d].label == checkbox_key){
 
								new_data.push(cur_data[d]);
 
							}
 
						}			                
 
	    	        }
 
	                else{
 
		                //push dummy data to not hide the label
 
						new_data.push(getDummyData(checkbox_key));
 
			        }
 
		        }
 
					        
 
		    	var new_options = YAHOO.lang.merge(plot_options, {
 
		            xaxis: { 
 
		  	      		min: cur_ranges.xaxis.from, 
 
		  	      		max: cur_ranges.xaxis.to,
 
		  	      		mode:"time",
 
		  	      		timeformat: "%d/%m",
 
		        	},
 
		    	});
 
		    	if (!new_data){
 
					new_data = [[0,1]];
 
				}
 
		    	// do the zooming
 
		       plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
 
		       
 
		       plot.subscribe("plotselected", plotselected);
 
	
 
		       //resubscribe plothover
 
		       plot.subscribe("plothover", plothover);
 
		        
 
		       // don't fire event on the overview to prevent eternal loop
 
		       overview.setSelection(cur_ranges, true);
 
	
 
		    }
 
		    
 
			/**
 
		     * plot only selected items from overview
 
		     * @param ranges
 
		     * @returns
 
		     */
 
		    function plotselected(ranges,cur_data) {
 
			    //updates the data for new plot
 
	    		data = getDataAccordingToRanges(ranges);
 
	    		generateCheckboxes(data);
 
	    		
 
		    	var new_options = YAHOO.lang.merge(plot_options, {
 
		            xaxis: { 
 
		  	      		min: ranges.xaxis.from, 
 
		  	      		max: ranges.xaxis.to,
 
		  	      		mode:"time",
 
		  	      		timeformat: "%d/%m",
 
		        	},
 
		        	yaxis: { 
 
                        min: ranges.yaxis.from, 
 
                        max: ranges.yaxis.to,
 
                    },
 
                    		        	
 
		    	});
 
		    	// do the zooming
 
		        plot = YAHOO.widget.Flot(plotContainer, data, new_options);
 

	
 
		        plot.subscribe("plotselected", plotselected);
 

	
 
		        //resubscribe plothover
 
		        plot.subscribe("plothover", plothover);
 
		        
 
		        // don't fire event on the overview to prevent eternal loop
 
		        overview.setSelection(ranges, true);
 

	
 
		        //resubscribe choiced
 
		        YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
 
		    }
 
		    
 
		    var previousPoint = null;
 

	
 
			function plothover(o) {
 
		        var pos = o.pos;
 
		        var item = o.item;
 
		        
 
		        //YUD.get("x").innerHTML = pos.x.toFixed(2);
 
		        //YUD.get("y").innerHTML = pos.y.toFixed(2);
 
		        if (item) {
 
		            if (previousPoint != item.datapoint) {
 
		                previousPoint = item.datapoint;
 
		                
 
		                var tooltip = YUD.get("tooltip");
 
		                if(tooltip) {
 
		                	  tooltip.parentNode.removeChild(tooltip);
 
		                }
 
		                var x = item.datapoint.x.toFixed(2);
 
		                var y = item.datapoint.y.toFixed(2);
 
						
 
		                if (!item.series.label){
 
		                    item.series.label = 'commits';
 
		                }
 
		                var d = new Date(x*1000);
 
		                var fd = d.toDateString()
 
		                var nr_commits = parseInt(y);
 
		                
 
		                var cur_data = dataset[item.series.label].data[item.dataIndex];
 
		                var added = cur_data.added;
 
		                var changed = cur_data.changed;
 
		                var removed = cur_data.removed;
 
		                
 
		                var nr_commits_suffix = " ${_('commits')} ";
 
		                var added_suffix = " ${_('files added')} ";
 
		                var changed_suffix = " ${_('files changed')} ";
 
		                var removed_suffix = " ${_('files removed')} ";
 

	
 
		                
 
		                if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
 
						if(added==1){added_suffix=" ${_('file added')} ";}
 
						if(changed==1){changed_suffix=" ${_('file changed')} ";}
 
						if(removed==1){removed_suffix=" ${_('file removed')} ";}
 
										                
 
		                showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
 
								 +'<br/>'+
 
		                         nr_commits + nr_commits_suffix+'<br/>'+
 
		                         added + added_suffix +'<br/>'+
 
		                         changed + changed_suffix + '<br/>'+
 
		                         removed + removed_suffix + '<br/>');
 
		            }
 
		        }
 
		        else {
 
		        	  var tooltip = YUD.get("tooltip");
 
		        	  
 
			          if(tooltip) {
 
			                tooltip.parentNode.removeChild(tooltip);
 
			          }
 
		            previousPoint = null;
 
		        }
 
		    }
 
			
 
		    /**
 
		     * MAIN EXECUTION
 
		     */
 
			
 
			var data = getDataAccordingToRanges(initial_ranges);
 
			generateCheckboxes(data);
 
			
 
		    //main plot
 
		    var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
 
		    
 
			//overview
 
			var overview = YAHOO.widget.Flot(overviewContainer, overview_dataset, overview_options);
 
			
 
			//show initial selection on overview
 
			overview.setSelection(initial_ranges);    
 
			
 
		    plot.subscribe("plotselected", plotselected);
 
		    
 
		    overview.subscribe("plotselected", function (ranges) {
 
		        plot.setSelection(ranges);
 
		    });		
 
				
 
		    plot.subscribe("plothover", plothover);
 

	
 
		    YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
 
		}
 
			SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});		
 
		</script>
 

	
 
    </div>
 
</div>    
 

	
 
<div class="box">    
 
    <div class="title">
 
        <div class="breadcrumbs">${h.link_to(_('Shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}</div>
 
    </div>    
 
    <div class="table">
 
        <div id="shortlog_data">
 
            <%include file='../shortlog/shortlog_data.html'/>
 
        </div>
 
        ##%if c.repo_changesets:
 
        ##	${h.link_to(_('show more'),h.url('changelog_home',repo_name=c.repo_name))}
 
        ##%endif
 
    </div>
 
</div>
 
<div class="box">    
 
    <div class="title">
 
        <div class="breadcrumbs">${h.link_to(_('Last ten tags'),h.url('tags_home',repo_name=c.repo_name))}</div>
 
    </div>    
 
    <div class="table">
 
        <%include file='../tags/tags_data.html'/>
 
        %if c.repo_changesets:
 
        	${h.link_to(_('show more'),h.url('tags_home',repo_name=c.repo_name))}
 
        %endif
 
    </div>
 
</div>
 
<div class="box">
 
    <div class="title">
 
        <div class="breadcrumbs">${h.link_to(_('Last ten branches'),h.url('branches_home',repo_name=c.repo_name))}</div>
 
    </div>    
 
    <div class="table">
 
        <%include file='../branches/branches_data.html'/>
 
        %if c.repo_changesets:
 
        	${h.link_to(_('show more'),h.url('branches_home',repo_name=c.repo_name))}
 
        %endif
 
    </div>      
 
</div> 
 

	
 
</%def>    
 
\ No newline at end of file
0 comments (0 inline, 0 general)