changeset 1115:fcb5054937f6 beta

merge with optional gravatars
author Marcin Kuzminski <marcin@python-works.com>
date Sat, 05 Mar 2011 23:02:15 +0100
parents 4de3fa6290a7 (diff) 5351a3a32381 (current diff)
children 716911af91e1
files rhodecode/lib/helpers.py
diffstat 15 files changed, 321 insertions(+), 231 deletions(-) [+]
line wrap: on
line diff
--- a/rhodecode/config/routing.py	Wed Mar 02 11:50:13 2011 +0100
+++ b/rhodecode/config/routing.py	Sat Mar 05 23:02:15 2011 +0100
@@ -89,6 +89,10 @@
         m.connect('repo_public_journal', "/repos_public_journal/{repo_name:.*}",
              action="repo_public_journal", conditions=dict(method=["PUT"],
                                                         function=check_repo))
+        m.connect('repo_pull', "/repo_pull/{repo_name:.*}",
+             action="repo_pull", conditions=dict(method=["PUT"],
+                                                        function=check_repo))
+
 
     #ADMIN USER REST ROUTES
     routes_map.resource('user', 'users', controller='admin/users', path_prefix='/_admin')
--- a/rhodecode/controllers/admin/repos.py	Wed Mar 02 11:50:13 2011 +0100
+++ b/rhodecode/controllers/admin/repos.py	Sat Mar 05 23:02:15 2011 +0100
@@ -42,12 +42,11 @@
 from rhodecode.lib.base import BaseController, render
 from rhodecode.lib.utils import invalidate_cache, action_logger, repo_name_slug
 from rhodecode.lib.helpers import get_token
-from rhodecode.model.db import User, Repository, UserFollowing
+from rhodecode.model.db import User, Repository, UserFollowing, Group
 from rhodecode.model.forms import RepoForm
 from rhodecode.model.scm import ScmModel
 from rhodecode.model.repo import RepoModel
 
-
 log = logging.getLogger(__name__)
 
 class ReposController(BaseController):
@@ -64,6 +63,82 @@
         c.admin_username = session.get('admin_username')
         super(ReposController, self).__before__()
 
+
+
+    def __load_data(self, repo_name):
+        """
+        Load defaults settings for edit, and update
+        
+        :param repo_name:
+        """
+        repo, dbrepo = ScmModel().get(repo_name, retval='repo')
+
+        repo_model = RepoModel()
+        c.repo_info = repo_model.get_by_repo_name(repo_name)
+
+        if c.repo_info is None:
+            h.flash(_('%s repository is not mapped to db perhaps'
+                      ' it was created or renamed from the filesystem'
+                      ' please run the application again'
+                      ' in order to rescan repositories') % repo_name,
+                      category='error')
+
+            return redirect(url('repos'))
+
+
+
+        c.repo_groups = [('', '')]
+        c.repo_groups.extend([(x.group_id, x.group_name) for x in self.sa.query(Group).all()])
+
+        c.default_user_id = User.by_username('default').user_id
+        c.in_public_journal = self.sa.query(UserFollowing)\
+            .filter(UserFollowing.user_id == c.default_user_id)\
+            .filter(UserFollowing.follows_repository == c.repo_info).scalar()
+
+        if c.repo_info.stats:
+            last_rev = c.repo_info.stats.stat_on_revision
+        else:
+            last_rev = 0
+        c.stats_revision = last_rev
+
+        c.repo_last_rev = repo.count() - 1 if repo.revisions else 0
+
+        if last_rev == 0 or c.repo_last_rev == 0:
+            c.stats_percentage = 0
+        else:
+            c.stats_percentage = '%.2f' % ((float((last_rev)) /
+                                            c.repo_last_rev) * 100)
+
+        c.users_array = repo_model.get_users_js()
+        c.users_groups_array = repo_model.get_users_groups_js()
+
+        defaults = c.repo_info.get_dict()
+        group, repo_name = c.repo_info.groups_and_repo
+        defaults['repo_name'] = repo_name
+        defaults['repo_group'] = getattr(group, 'group_id', None)
+        #fill owner
+        if c.repo_info.user:
+            defaults.update({'user':c.repo_info.user.username})
+        else:
+            replacement_user = self.sa.query(User)\
+            .filter(User.admin == True).first().username
+            defaults.update({'user':replacement_user})
+
+
+        #fill repository users
+        for p in c.repo_info.repo_to_perm:
+            defaults.update({'u_perm_%s' % p.user.username:
+                             p.permission.permission_name})
+
+        #fill repository groups
+        for p in c.repo_info.users_group_to_perm:
+            defaults.update({'g_perm_%s' % p.users_group.users_group_name:
+                             p.permission.permission_name})
+
+
+        return defaults
+
+
     @HasPermissionAllDecorator('hg.admin')
     def index(self, format='html'):
         """GET /repos: All items in the collection"""
@@ -78,12 +153,18 @@
         POST /repos: Create a new item"""
         # url('repos')
         repo_model = RepoModel()
-        _form = RepoForm()()
+        c.repo_groups = [('', '')]
+        c.repo_groups.extend([(x.group_id, x.group_name) for x in self.sa.query(Group).all()])
         form_result = {}
         try:
-            form_result = _form.to_python(dict(request.POST))
+            form_result = RepoForm()(repo_groups=c.repo_groups).to_python(dict(request.POST))
             repo_model.create(form_result, c.rhodecode_user)
-            h.flash(_('created repository %s') % form_result['repo_name'],
+            if form_result['clone_uri']:
+                h.flash(_('created repository %s from %s') \
+                    % (form_result['repo_name'], form_result['clone_uri']),
+                    category='success')
+            else:
+                h.flash(_('created repository %s') % form_result['repo_name'],
                     category='success')
 
             if request.POST.get('user_created'):
@@ -94,7 +175,10 @@
                               form_result['repo_name'], '', self.sa)
 
         except formencode.Invalid, errors:
+
             c.new_repo = errors.value['repo_name']
+            c.repo_groups = [('', '')]
+            c.repo_groups.extend([(x.group_id, x.group_name) for x in self.sa.query(Group).all()])
 
             if request.POST.get('user_created'):
                 r = render('admin/repos/repo_add_create_repository.html')
@@ -122,7 +206,8 @@
         """GET /repos/new: Form to create a new item"""
         new_repo = request.GET.get('repo', '')
         c.new_repo = repo_name_slug(new_repo)
-
+        c.repo_groups = [('', '')]
+        c.repo_groups.extend([(x.group_id, x.group_name) for x in self.sa.query(Group).all()])
         return render('admin/repos/repo_add.html')
 
     @HasPermissionAllDecorator('hg.admin')
@@ -138,7 +223,6 @@
         repo_model = RepoModel()
         changed_name = repo_name
         _form = RepoForm(edit=True, old_data={'repo_name':repo_name})()
-
         try:
             form_result = _form.to_python(dict(request.POST))
             repo_model.update(repo_name, form_result)
@@ -150,34 +234,11 @@
                               changed_name, '', self.sa)
 
         except formencode.Invalid, errors:
-            c.repo_info = repo_model.get_by_repo_name(repo_name)
-
-            if c.repo_info.stats:
-                last_rev = c.repo_info.stats.stat_on_revision
-            else:
-                last_rev = 0
-            c.stats_revision = last_rev
-            repo, dbrepo = ScmModel().get(repo_name, retval='repo')
-            c.repo_last_rev = repo.count() - 1 if repo.revisions else 0
-
-            c.default_user_id = User.by_username('default').user_id
-            c.in_public_journal = self.sa.query(UserFollowing)\
-                .filter(UserFollowing.user_id == c.default_user_id)\
-                .filter(UserFollowing.follows_repository == c.repo_info).scalar()
-
-            if last_rev == 0:
-                c.stats_percentage = 0
-            else:
-                c.stats_percentage = '%.2f' % ((float((last_rev)) /
-                                                c.repo_last_rev) * 100)
-
-            c.users_array = repo_model.get_users_js()
-            c.users_groups_array = repo_model.get_users_groups_js()
-
-            errors.value.update({'user':c.repo_info.user.username})
+            defaults = self.__load_data(repo_name)
+            defaults.update(errors.value)
             return htmlfill.render(
                 render('admin/repos/repo_edit.html'),
-                defaults=errors.value,
+                defaults=defaults,
                 errors=errors.error_dict or {},
                 prefix_error=False,
                 encoding="UTF-8")
@@ -186,7 +247,6 @@
             log.error(traceback.format_exc())
             h.flash(_('error occurred during update of repository %s') \
                     % repo_name, category='error')
-
         return redirect(url('edit_repo', repo_name=changed_name))
 
     @HasPermissionAllDecorator('hg.admin')
@@ -314,7 +374,22 @@
             h.flash(_('Token mismatch'), category='error')
         return redirect(url('edit_repo', repo_name=repo_name))
 
+    @HasPermissionAllDecorator('hg.admin')
+    def repo_pull(self, repo_name):
+        """
+        Runs task to update given repository with remote changes,
+        ie. make pull on remote location
+        
+        :param repo_name:
+        """
+        try:
+            ScmModel().pull_changes(repo_name, c.rhodecode_user.username)
+            h.flash(_('Pulled from remote location'), category='success')
+        except Exception, e:
+            h.flash(_('An error occurred during pull from remote location'),
+                    category='error')
 
+        return redirect(url('edit_repo', repo_name=repo_name))
 
     @HasPermissionAllDecorator('hg.admin')
     def show(self, repo_name, format='html'):
@@ -325,62 +400,7 @@
     def edit(self, repo_name, format='html'):
         """GET /repos/repo_name/edit: Form to edit an existing item"""
         # url('edit_repo', repo_name=ID)
-        repo, dbrepo = ScmModel().get(repo_name, retval='repo')
-
-        repo_model = RepoModel()
-        c.repo_info = repo_model.get_by_repo_name(repo_name)
-
-        if c.repo_info is None:
-            h.flash(_('%s repository is not mapped to db perhaps'
-                      ' it was created or renamed from the filesystem'
-                      ' please run the application again'
-                      ' in order to rescan repositories') % repo_name,
-                      category='error')
-
-            return redirect(url('repos'))
-
-        c.default_user_id = User.by_username('default').user_id
-        c.in_public_journal = self.sa.query(UserFollowing)\
-            .filter(UserFollowing.user_id == c.default_user_id)\
-            .filter(UserFollowing.follows_repository == c.repo_info).scalar()
-
-        if c.repo_info.stats:
-            last_rev = c.repo_info.stats.stat_on_revision
-        else:
-            last_rev = 0
-        c.stats_revision = last_rev
-
-        c.repo_last_rev = repo.count() - 1 if repo.revisions else 0
-
-        if last_rev == 0 or c.repo_last_rev == 0:
-            c.stats_percentage = 0
-        else:
-            c.stats_percentage = '%.2f' % ((float((last_rev)) /
-                                            c.repo_last_rev) * 100)
-
-        c.users_array = repo_model.get_users_js()
-        c.users_groups_array = repo_model.get_users_groups_js()
-
-        defaults = c.repo_info.get_dict()
-
-        #fill owner
-        if c.repo_info.user:
-            defaults.update({'user':c.repo_info.user.username})
-        else:
-            replacement_user = self.sa.query(User)\
-            .filter(User.admin == True).first().username
-            defaults.update({'user':replacement_user})
-
-
-        #fill repository users
-        for p in c.repo_info.repo_to_perm:
-            defaults.update({'u_perm_%s' % p.user.username:
-                             p.permission.permission_name})
-
-        #fill repository groups
-        for p in c.repo_info.users_group_to_perm:
-            defaults.update({'g_perm_%s' % p.users_group.users_group_name:
-                             p.permission.permission_name})
+        defaults = self.__load_data(repo_name)
 
         return htmlfill.render(
             render('admin/repos/repo_edit.html'),
--- a/rhodecode/controllers/admin/settings.py	Wed Mar 02 11:50:13 2011 +0100
+++ b/rhodecode/controllers/admin/settings.py	Sat Mar 05 23:02:15 2011 +0100
@@ -42,7 +42,7 @@
 from rhodecode.lib.celerylib import tasks, run_task
 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
     set_rhodecode_config, repo_name_slug
-from rhodecode.model.db import RhodeCodeUi, Repository
+from rhodecode.model.db import RhodeCodeUi, Repository, Group
 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
     ApplicationUiSettingsForm
 from rhodecode.model.scm import ScmModel
@@ -321,7 +321,8 @@
         """GET /_admin/create_repository: Form to create a new item"""
         new_repo = request.GET.get('repo', '')
         c.new_repo = repo_name_slug(new_repo)
-
+        c.repo_groups = [('', '')]
+        c.repo_groups.extend([(x.group_id, x.group_name) for x in self.sa.query(Group).all()])
         return render('admin/repos/repo_add_create_repository.html')
 
     def get_hg_ui_settings(self):
--- a/rhodecode/lib/helpers.py	Wed Mar 02 11:50:13 2011 +0100
+++ b/rhodecode/lib/helpers.py	Sat Mar 05 23:02:15 2011 +0100
@@ -508,6 +508,7 @@
            'admin_forked_repo':(_('[forked] repository'), None),
            'admin_updated_repo':(_('[updated] repository'), None),
            'push':(_('[pushed] into'), get_cs_links),
+           'push_remote':(_('[pulled from remote] into'), get_cs_links),
            'pull':(_('[pulled] from'), None),
            'started_following_repo':(_('[started following] repository'), None),
            'stopped_following_repo':(_('[stopped following] repository'), None),
@@ -519,9 +520,10 @@
     else:
         action = action_str[0].replace('[', '<span class="journal_highlight">')\
                    .replace(']', '</span>')
+
     action_params_func = lambda :""
 
-    if action_str[1] is not None:
+    if callable(action_str[1]):
         action_params_func = action_str[1]
 
     return [literal(action), action_params_func]
@@ -534,7 +536,7 @@
     if len(x) > 1:
         action, action_params = x
 
-    tmpl = """<img src="%s/%s" alt="%s"/>"""
+    tmpl = """<img src="%s%s" alt="%s"/>"""
     map = {'user_deleted_repo':'database_delete.png',
            'user_created_repo':'database_add.png',
            'user_forked_repo':'arrow_divide.png',
@@ -544,6 +546,7 @@
            'admin_forked_repo':'arrow_divide.png',
            'admin_updated_repo':'database_edit.png',
            'push':'script_add.png',
+           'push_remote':'connect.png',
            'pull':'down_16.png',
            'started_following_repo':'heart_add.png',
            'stopped_following_repo':'heart_delete.png',
--- a/rhodecode/lib/hooks.py	Wed Mar 02 11:50:13 2011 +0100
+++ b/rhodecode/lib/hooks.py	Sat Mar 05 23:02:15 2011 +0100
@@ -91,7 +91,7 @@
     extra_params = dict(repo.ui.configitems('rhodecode_extras'))
     username = extra_params['username']
     repository = extra_params['repository']
-    action = 'push:%s'
+    action = extra_params['action'] + ':%s'
     node = kwargs['node']
 
     def get_revs(repo, rev_opt):
--- a/rhodecode/model/db.py	Wed Mar 02 11:50:13 2011 +0100
+++ b/rhodecode/model/db.py	Sat Mar 05 23:02:15 2011 +0100
@@ -50,8 +50,8 @@
     __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)
+    app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+    app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
     def __init__(self, k='', v=''):
         self.app_settings_name = k
@@ -65,9 +65,9 @@
     __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_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+    ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+    ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
     ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
 
 
@@ -75,15 +75,16 @@
     __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)
+    username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+    password = Column("password", String(length=255, 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)
+    name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+    lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+    email = Column("email", String(length=255, 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)
-    ldap_dn = Column("ldap_dn", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+    ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+    api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
     user_log = relationship('UserLog', cascade='all')
     user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
@@ -134,9 +135,9 @@
     user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
     user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
     repository_id = Column("repository_id", Integer(length=None, convert_unicode=False, assert_unicode=None), ForeignKey('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)
+    repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+    user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+    action = Column("action", UnicodeText(length=1200000, 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)
 
     @property
@@ -152,7 +153,7 @@
     __table_args__ = {'useexisting':True}
 
     users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
-    users_group_name = Column("users_group_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
+    users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
     users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
 
     members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
@@ -178,16 +179,18 @@
     __mapper_args__ = {'extension':RepositoryMapper()}
 
     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='hg')
+    repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
+    clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
+    repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
     user_id = Column("user_id", Integer(), ForeignKey('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)
     enable_downloads = Column("downloads", 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)
+    description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
     fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
     group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
 
+
     user = relationship('User')
     fork = relationship('Repository', remote_side=repo_id)
     group = relationship('Group')
@@ -207,13 +210,19 @@
     def by_repo_name(cls, repo_name):
         return Session.query(cls).filter(cls.repo_name == repo_name).one()
 
+    @property
+    def groups_and_repo(self):
+        just_name = self.repo_name.split('/')[-1]
+
+        return self.group, just_name
+
 
 class Group(Base):
     __tablename__ = 'groups'
     __table_args__ = (UniqueConstraint('group_name'), {'useexisting':True},)
 
     group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
-    group_name = Column("group_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
+    group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
     group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
 
     parent_group = relationship('Group', remote_side=group_id)
@@ -231,8 +240,8 @@
     __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)
+    permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+    permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
     def __repr__(self):
         return "<%s('%s:%s')>" % (self.__class__.__name__,
@@ -292,9 +301,9 @@
     stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
     repository_id = Column("repository_id", Integer(), ForeignKey('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 = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
     commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
-    languages = Column("languages", LargeBinary(), nullable=False)#JSON data
+    languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
 
     repository = relationship('Repository', single_parent=True)
 
@@ -318,8 +327,8 @@
     __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_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+    cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
     cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
 
 
--- a/rhodecode/model/forms.py	Wed Mar 02 11:50:13 2011 +0100
+++ b/rhodecode/model/forms.py	Sat Mar 05 23:02:15 2011 +0100
@@ -450,17 +450,21 @@
         email = All(ValidSystemEmail(), Email(not_empty=True))
     return _PasswordResetForm
 
-def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
+def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
+             repo_groups=[]):
     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))
+        clone_uri = UnicodeString(strip=True, min=1, not_empty=False)
+        repo_group = OneOf(repo_groups)
+        repo_type = OneOf(supported_backends)
         description = UnicodeString(strip=True, min=1, not_empty=True)
         private = StringBoolean(if_missing=False)
         enable_statistics = StringBoolean(if_missing=False)
         enable_downloads = StringBoolean(if_missing=False)
-        repo_type = OneOf(supported_backends)
+
         if edit:
             #this is repo owner
             user = All(Int(not_empty=True), ValidRepoUser)
--- a/rhodecode/model/repo.py	Wed Mar 02 11:50:13 2011 +0100
+++ b/rhodecode/model/repo.py	Sat Mar 05 23:02:15 2011 +0100
@@ -234,7 +234,8 @@
             self.sa.add(repo_to_perm)
 
             if not just_db:
-                self.__create_repo(repo_name, form_data['repo_type'])
+                self.__create_repo(repo_name, form_data['repo_type'],
+                                   form_data['clone_uri'])
 
             self.sa.commit()
 
@@ -299,7 +300,7 @@
             raise
 
 
-    def __create_repo(self, repo_name, alias):
+    def __create_repo(self, repo_name, alias, clone_uri=False):
         """
         makes repository on filesystem
         :param repo_name:
@@ -308,9 +309,10 @@
         from rhodecode.lib.utils import check_repo
         repo_path = os.path.join(self.repos_path, repo_name)
         if check_repo(repo_name, self.repos_path):
-            log.info('creating repo %s in %s', repo_name, repo_path)
+            log.info('creating repo %s in %s @ %s', repo_name, repo_path,
+                    clone_uri)
             backend = get_backend(alias)
-            backend(repo_path, create=True)
+            backend(repo_path, create=True, src_url=clone_uri)
 
     def __rename_repo(self, old, new):
         """
--- a/rhodecode/model/scm.py	Wed Mar 02 11:50:13 2011 +0100
+++ b/rhodecode/model/scm.py	Sat Mar 05 23:02:15 2011 +0100
@@ -232,8 +232,6 @@
 
         return r, dbr
 
-
-
     def mark_for_invalidation(self, repo_name):
         """Puts cache invalidation task into db for 
         further global cache invalidation
@@ -359,6 +357,25 @@
                         == RepoModel().get_by_repo_name(repo_id)).count()
 
 
+    def pull_changes(self, repo_name, username):
+        repo, dbrepo = self.get(repo_name, retval='all')
+
+        try:
+            extras = {'ip':'',
+                      'username':username,
+                      'action':'push_remote',
+                      'repository':repo_name}
+
+            #inject ui extra param to log this action via push logger        
+            for k, v in extras.items():
+                repo._repo.ui.setconfig('rhodecode_extras', k, v)
+
+            repo.pull(dbrepo.clone_uri)
+            self.mark_for_invalidation(repo_name)
+        except:
+            log.error(traceback.format_exc())
+            raise
+
     def get_unread_journal(self):
         return self.sa.query(UserLog).count()
 
--- a/rhodecode/public/css/style.css	Wed Mar 02 11:50:13 2011 +0100
+++ b/rhodecode/public/css/style.css	Sat Mar 05 23:02:15 2011 +0100
@@ -1964,6 +1964,14 @@
 text-align:left;
 }
 
+.pull_icon {
+background:url("../images/icons/connect.png") no-repeat scroll 3px;
+height:16px;
+padding-left:20px;
+padding-top:1px;
+text-align:left;
+}
+
 .rss_icon {
 background:url("../images/icons/rss_16.png") no-repeat scroll 3px;
 height:16px;
--- a/rhodecode/templates/admin/repos/repo_add.html	Wed Mar 02 11:50:13 2011 +0100
+++ b/rhodecode/templates/admin/repos/repo_add.html	Sat Mar 05 23:02:15 2011 +0100
@@ -16,53 +16,13 @@
 <%def name="page_nav()">
 	${self.menu('admin')}
 </%def>
+
 <%def name="main()">
-<div class="box">
-    <!-- box / title -->
-    <div class="title">
-        ${self.breadcrumbs()}      
-    </div>
-    ${h.form(url('repos'))}
-    <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',c.new_repo,class_="small")}
-	            </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_="small")}
-                </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="buttons">
-	          ${h.submit('add','add',class_="ui-button")}
-	        </div>                                                          
-        </div>
-    </div>    
-    ${h.end_form()}    
-</div>
-</%def>   
+	<div class="box">
+	    <!-- box / title -->
+	    <div class="title">
+	        ${self.breadcrumbs()}      
+	    </div>
+        <%include file="repo_add_base.html"/>
+    </div>        
+</%def>   
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rhodecode/templates/admin/repos/repo_add_base.html	Sat Mar 05 23:02:15 2011 +0100
@@ -0,0 +1,61 @@
+## -*- coding: utf-8 -*-
+
+${h.form(url('repos'))}
+<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',c.new_repo,class_="small")}
+            </div>
+         </div>
+        <div class="field">
+            <div class="label">
+                <label for="clone_uri">${_('Clone from')}:</label>
+            </div>
+            <div class="input">
+                ${h.text('clone_uri',class_="small")}
+            </div>
+         </div>             
+         <div class="field">
+             <div class="label">
+                 <label for="repo_group">${_('Repository group')}:</label>
+             </div>
+             <div class="input">
+                 ${h.select('repo_group','',c.repo_groups,class_="medium")}
+             <span>${h.link_to(_('add new group'),h.url(''))}</span>
+             </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_="small")}
+            </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="buttons">
+          ${h.submit('add','add',class_="ui-button")}
+        </div>                                                          
+    </div>
+</div>    
+${h.end_form()}    
--- a/rhodecode/templates/admin/repos/repo_add_create_repository.html	Wed Mar 02 11:50:13 2011 +0100
+++ b/rhodecode/templates/admin/repos/repo_add_create_repository.html	Sat Mar 05 23:02:15 2011 +0100
@@ -12,54 +12,13 @@
 <%def name="page_nav()">
 	${self.menu('admin')}
 </%def>
+
 <%def name="main()">
-<div class="box">
-    <!-- box / title -->
-    <div class="title">
-        ${self.breadcrumbs()}      
-    </div>
-    ${h.form(url('repos'))}
-    <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',c.new_repo,class_="small")}
-	                ${h.hidden('user_created','True')}
-	            </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_="small")}
-                </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="buttons">
-	          ${h.submit('add','add',class_="ui-button")}
-	        </div>                                                          
+    <div class="box">
+        <!-- box / title -->
+        <div class="title">
+            ${self.breadcrumbs()}      
         </div>
-    </div>    
-    ${h.end_form()}    
-</div>
-</%def>   
+        <%include file="repo_add_base.html"/>
+    </div>        
+</%def>
\ No newline at end of file
--- a/rhodecode/templates/admin/repos/repo_edit.html	Wed Mar 02 11:50:13 2011 +0100
+++ b/rhodecode/templates/admin/repos/repo_edit.html	Sat Mar 05 23:02:15 2011 +0100
@@ -34,7 +34,24 @@
                 <div class="input">
                     ${h.text('repo_name',class_="medium")}
                 </div>
-             </div>
+           </div>
+	       <div class="field">
+	           <div class="label">
+	               <label for="clone_uri">${_('Clone uri')}:</label>
+	           </div>
+	           <div class="input">
+	               ${h.text('clone_uri',class_="small")}
+	           </div>
+	        </div>             
+	        <div class="field">
+	            <div class="label">
+	                <label for="repo_group">${_('Repository group')}:</label>
+	            </div>
+	            <div class="input">
+	                ${h.select('repo_group','',c.repo_groups,class_="medium")}
+	            <span>${h.link_to(_('add new group'),h.url(''))}</span>
+	            </div>
+	        </div>         
             <div class="field">
                 <div class="label">
                     <label for="repo_type">${_('Type')}:</label>
@@ -42,7 +59,7 @@
                 <div class="input">
                     ${h.select('repo_type','hg',c.backends,class_="medium")}
                 </div>
-             </div>             
+            </div>             
             <div class="field">
                 <div class="label label-textarea">
                     <label for="description">${_('Description')}:</label>
@@ -297,12 +314,11 @@
     </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">
+               <div class="field" style="border:none">
                <ul>
                     <li>${_('Fetched to rev')}: ${c.stats_revision}/${c.repo_last_rev}</li>
                     <li>${_('Percentage of stats gathered')}: ${c.stats_percentage} %</li>
@@ -313,6 +329,22 @@
         </div>                    
         ${h.end_form()}
         
+        %if c.repo_info.clone_uri:
+        <h3>${_('Remote')}</h3>
+        ${h.form(url('repo_pull', repo_name=c.repo_info.repo_name),method='put')}
+        <div class="form">
+           <div class="fields">
+               ${h.submit('remote_pull_%s' % c.repo_info.repo_name,_('Pull changes from remote location'),class_="pull_icon action_button",onclick="return confirm('Confirm to pull changes from remote side');")}
+               <div class="field" style="border:none">
+               <ul>
+                    <li><a href="${c.repo_info.clone_uri}">${c.repo_info.clone_uri}</a></li>
+               </ul> 
+               </div>              
+           </div>
+        </div>                    
+        ${h.end_form()}
+        %endif
+        
         <h3>${_('Cache')}</h3>
         ${h.form(url('repo_cache', repo_name=c.repo_info.repo_name),method='delete')}
         <div class="form">
--- a/rhodecode/templates/summary/summary.html	Wed Mar 02 11:50:13 2011 +0100
+++ b/rhodecode/templates/summary/summary.html	Sat Mar 05 23:02:15 2011 +0100
@@ -65,7 +65,17 @@
 		            	${_('Fork of')} ${c.dbrepo.fork.repo_name}
 		            	</a>
 		            	</span>
-		            %endif			      
+		            %endif
+					%if c.dbrepo.clone_uri:
+                        <span style="margin-top:5px">
+                        <a href="${h.url(str(c.dbrepo.clone_uri))}">
+                        <img class="icon" alt="${_('remote clone')}"
+                        title="${_('Clone from')} ${c.dbrepo.clone_uri}" 
+                        src="${h.url("/images/icons/connect.png")}"/>
+                        ${_('Clone from')} ${c.dbrepo.clone_uri}
+                        </a>
+                        </span>					
+					%endif		            		      
 			  </div>
 			 </div>