# HG changeset patch # User Marcin Kuzminski # Date 1275862713 -7200 # Node ID 0e5455fda8fd1c3e8dc0b3510e480819f9445f89 # Parent 0d68a749db33733412c00fd1aebee8c0ebdce9a6 Implemented basic repository managment. Implemented repo2db mappings, model, helpers updates and code cleanups diff -r 0d68a749db33 -r 0e5455fda8fd pylons_app/config/environment.py --- a/pylons_app/config/environment.py Sun Jun 06 23:35:21 2010 +0200 +++ b/pylons_app/config/environment.py Mon Jun 07 00:18:33 2010 +0200 @@ -6,6 +6,7 @@ from pylons_app.lib.auth import set_available_permissions from pylons_app.lib.utils import repo2db_mapper from pylons_app.model import init_model +from pylons_app.model.hg_model import _get_repos_cached_initial from sqlalchemy import engine_from_config import logging import os @@ -60,7 +61,7 @@ sa_engine_db1 = engine_from_config(config, 'sqlalchemy.db1.') init_model(sa_engine_db1) - repo2db_mapper() + repo2db_mapper(_get_repos_cached_initial(config['pylons.app_globals'])) set_available_permissions(config) # CONFIGURATION OPTIONS HERE (note: all config options will override # any Pylons config options) diff -r 0d68a749db33 -r 0e5455fda8fd pylons_app/controllers/repos.py --- a/pylons_app/controllers/repos.py Sun Jun 06 23:35:21 2010 +0200 +++ b/pylons_app/controllers/repos.py Mon Jun 07 00:18:33 2010 +0200 @@ -2,7 +2,6 @@ # encoding: utf-8 # repos controller for pylons # Copyright (C) 2009-2010 Marcin Kuzminski - # 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 @@ -22,20 +21,25 @@ admin controller for pylons @author: marcink """ -import logging +from operator import itemgetter from pylons import request, response, session, tmpl_context as c, url, \ app_globals as g from pylons.controllers.util import abort, redirect -from pylons_app.lib.auth import LoginRequired from pylons.i18n.translation import _ from pylons_app.lib import helpers as h +from pylons_app.lib.auth import LoginRequired from pylons_app.lib.base import BaseController, render -from pylons_app.lib.filters import clean_repo -from pylons_app.lib.utils import check_repo, invalidate_cache +from pylons_app.lib.utils import invalidate_cache +from pylons_app.model.repo_model import RepoModel from pylons_app.model.hg_model import HgModel +from pylons_app.model.forms import RepoForm +from pylons_app.model.meta import Session +from datetime import datetime +import formencode +from formencode import htmlfill +import logging import os import shutil -from operator import itemgetter log = logging.getLogger(__name__) class ReposController(BaseController): @@ -59,30 +63,33 @@ def create(self): """POST /repos: Create a new item""" # url('repos') - name = request.POST.get('name') - + repo_model = RepoModel() + _form = RepoForm()() try: - self._create_repo(name) - #clear our cached list for refresh with new repo + form_result = _form.to_python(dict(request.POST)) + repo_model.create(form_result, c.hg_app_user) invalidate_cache('cached_repo_list') - h.flash(_('created repository %s') % name, category='success') - except Exception as e: - log.error(e) - + h.flash(_('created repository %s') % form_result['repo_name'], + category='success') + + except formencode.Invalid as errors: + c.form_errors = errors.error_dict + c.new_repo = errors.value['repo_name'] + return htmlfill.render( + render('admin/repos/repo_add.html'), + defaults=errors.value, + encoding="UTF-8") + + except Exception: + h.flash(_('error occured during creation of repository %s') \ + % form_result['repo_name'], category='error') + return redirect('repos') - - def _create_repo(self, repo_name): - repo_path = os.path.join(g.base_path, repo_name) - if check_repo(repo_name, g.base_path): - log.info('creating repo %s in %s', repo_name, repo_path) - from vcs.backends.hg import MercurialRepository - MercurialRepository(repo_path, create=True) - def new(self, format='html'): """GET /repos/new: Form to create a new item""" new_repo = request.GET.get('repo', '') - c.new_repo = clean_repo(new_repo) + c.new_repo = h.repo_name_slug(new_repo) return render('admin/repos/repo_add.html') @@ -94,7 +101,26 @@ # h.form(url('repo', id=ID), # method='put') # url('repo', id=ID) - + repo_model = RepoModel() + _form = RepoForm(edit=True)() + try: + form_result = _form.to_python(dict(request.POST)) + repo_model.update(id, form_result) + invalidate_cache('cached_repo_list') + h.flash(_('Repository updated succesfully'), category='success') + + except formencode.Invalid as errors: + c.repo_info = repo_model.get(id) + c.form_errors = errors.error_dict + return htmlfill.render( + render('admin/repos/repo_edit.html'), + defaults=errors.value, + encoding="UTF-8") + except Exception: + h.flash(_('error occured during update of repository %s') \ + % form_result['repo_name'], category='error') + return redirect(url('repos')) + def delete(self, id): """DELETE /repos/id: Delete an existing item""" # Forms posted to this method should contain a hidden field: @@ -103,19 +129,25 @@ # h.form(url('repo', id=ID), # method='delete') # url('repo', id=ID) - from datetime import datetime - path = g.paths[0][1].replace('*', '') - rm_path = os.path.join(path, id) - log.info("Removing %s", rm_path) - shutil.move(os.path.join(rm_path, '.hg'), os.path.join(rm_path, 'rm__.hg')) - shutil.move(rm_path, os.path.join(path, 'rm__%s-%s' % (datetime.today(), id))) + + repo_model = RepoModel() + repo = repo_model.get(id) + if not repo: + h.flash(_('%s repository is not mapped to db perhaps' + ' it was moved or renamed please run the application again' + ' in order to rescan repositories') % id, category='error') - #clear our cached list for refresh with new repo - invalidate_cache('cached_repo_list') - h.flash(_('deleted repository %s') % rm_path, category='success') + return redirect(url('repos')) + try: + repo_model.delete(repo) + invalidate_cache('cached_repo_list') + h.flash(_('deleted repository %s') % id, category='success') + except Exception: + h.flash(_('An error occured during deletion of %s') % id, + category='error') + return redirect(url('repos')) - def show(self, id, format='html'): """GET /repos/id: Show a specific item""" # url('repo', id=ID) @@ -123,5 +155,13 @@ def edit(self, id, format='html'): """GET /repos/id/edit: Form to edit an existing item""" # url('edit_repo', id=ID) - c.new_repo = id - return render('admin/repos/repo_edit.html') + repo_model = RepoModel() + c.repo_info = repo_model.get(id) + defaults = c.repo_info.__dict__ + defaults.update({'user':c.repo_info.user.username}) + return htmlfill.render( + render('admin/repos/repo_edit.html'), + defaults=defaults, + encoding="UTF-8", + force_defaults=False + ) diff -r 0d68a749db33 -r 0e5455fda8fd pylons_app/controllers/users.py --- a/pylons_app/controllers/users.py Sun Jun 06 23:35:21 2010 +0200 +++ b/pylons_app/controllers/users.py Mon Jun 07 00:18:33 2010 +0200 @@ -23,7 +23,6 @@ @author: marcink """ import logging -from formencode import htmlfill from pylons import request, session, tmpl_context as c, url from pylons.controllers.util import abort, redirect from pylons.i18n.translation import _ @@ -34,6 +33,7 @@ from pylons_app.model.forms import UserForm from pylons_app.model.user_model import UserModel import formencode +from formencode import htmlfill log = logging.getLogger(__name__) @@ -65,15 +65,18 @@ try: form_result = login_form.to_python(dict(request.POST)) user_model.create(form_result) - h.flash(_('created user %s') % form_result['username'], category='success') - return redirect(url('users')) - + h.flash(_('created user %s') % form_result['username'], + category='success') except formencode.Invalid as errors: c.form_errors = errors.error_dict return htmlfill.render( render('admin/users/user_add.html'), defaults=errors.value, encoding="UTF-8") + except Exception: + h.flash(_('error occured during creation of user %s') \ + % form_result['username'], category='error') + return redirect(url('users')) def new(self, format='html'): """GET /users/new: Form to create a new item""" @@ -89,12 +92,11 @@ # method='put') # url('user', id=ID) user_model = UserModel() - login_form = UserForm(edit=True)() + _form = UserForm(edit=True)() try: - form_result = login_form.to_python(dict(request.POST)) + form_result = _form.to_python(dict(request.POST)) user_model.update(id, form_result) h.flash(_('User updated succesfully'), category='success') - return redirect(url('users')) except formencode.Invalid as errors: c.user = user_model.get_user(id) @@ -103,7 +105,12 @@ render('admin/users/user_edit.html'), defaults=errors.value, encoding="UTF-8") - + except Exception: + h.flash(_('error occured during update of user %s') \ + % form_result['username'], category='error') + + return redirect(url('users')) + def delete(self, id): """DELETE /users/id: Delete an existing item""" # Forms posted to this method should contain a hidden field: @@ -112,13 +119,14 @@ # h.form(url('user', id=ID), # method='delete') # url('user', id=ID) + user_model = UserModel() try: - self.sa.delete(self.sa.query(User).get(id)) - self.sa.commit() + user_model.delete(id) h.flash(_('sucessfully deleted user'), category='success') - except: - self.sa.rollback() - raise + except Exception: + h.flash(_('An error occured during deletion of user'), + category='error') + return redirect(url('users')) def show(self, id, format='html'): diff -r 0d68a749db33 -r 0e5455fda8fd pylons_app/lib/auth.py --- a/pylons_app/lib/auth.py Sun Jun 06 23:35:21 2010 +0200 +++ b/pylons_app/lib/auth.py Mon Jun 07 00:18:33 2010 +0200 @@ -65,6 +65,7 @@ A simple object that handles a mercurial username for authentication """ username = 'None' + user_id = None is_authenticated = False is_admin = False permissions = set() diff -r 0d68a749db33 -r 0e5455fda8fd pylons_app/lib/helpers.py --- a/pylons_app/lib/helpers.py Sun Jun 06 23:35:21 2010 +0200 +++ b/pylons_app/lib/helpers.py Mon Jun 07 00:18:33 2010 +0200 @@ -114,11 +114,9 @@ Return slug of name of repository """ slug = urlify(value) - for c in """=[]\;',/~!@#$%^&*()+{}|:""": + for c in """=[]\;'"<>,/~!@#$%^&*()+{}|:""": slug = slug.replace(c, '-') - print slug slug = recursive_replace(slug, '-') - print slug return slug files_breadcrumbs = _FilesBreadCrumbs() diff -r 0d68a749db33 -r 0e5455fda8fd pylons_app/lib/utils.py --- a/pylons_app/lib/utils.py Sun Jun 06 23:35:21 2010 +0200 +++ b/pylons_app/lib/utils.py Mon Jun 07 00:18:33 2010 +0200 @@ -28,6 +28,7 @@ import logging from mercurial import ui, config, hg from mercurial.error import RepoError +from pylons_app.model.db import Repository, User log = logging.getLogger(__name__) @@ -152,9 +153,28 @@ return '0' * 12 -def repo2db_mapper(): +def repo2db_mapper(initial_repo_list): + """ + maps all found repositories into db """ - scann all dirs for .hgdbid - if some dir doesn't have one generate one. - """ - pass + from pylons_app.model.meta import Session + sa = Session() + user = sa.query(User).filter(User.admin == True).first() + for name, repo in initial_repo_list.items(): + if not sa.query(Repository).get(name): + log.info('%s not found creating default', name) + try: + + new_repo = Repository() + new_repo.repo_name = name + desc = repo.description if repo.description != 'unknown' else \ + 'auto description for %s' % name + new_repo.description = desc + new_repo.user_id = user.user_id + new_repo.private = False + sa.add(new_repo) + sa.commit() + except: + sa.rollback() + raise + diff -r 0d68a749db33 -r 0e5455fda8fd pylons_app/model/db.py --- a/pylons_app/model/db.py Sun Jun 06 23:35:21 2010 +0200 +++ b/pylons_app/model/db.py Mon Jun 07 00:18:33 2010 +0200 @@ -1,11 +1,12 @@ from pylons_app.model.meta import Base from sqlalchemy.orm import relation, backref from sqlalchemy import * +from vcs.utils.lazy import LazyProperty class User(Base): __tablename__ = 'users' __table_args__ = {'useexisting':True} - user_id = Column("user_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=1) + user_id = Column("user_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True) username = Column("username", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) password = Column("password", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) active = Column("active", BOOLEAN(), nullable=True, unique=None, default=None) @@ -17,6 +18,10 @@ user_log = relation('UserLog') + @LazyProperty + def full_contact(self): + return '%s %s <%s>' % (self.name, self.lastname, self.email) + def __repr__(self): return "" % (self.user_id, self.username) @@ -24,7 +29,7 @@ __tablename__ = 'user_logs' __table_args__ = {'useexisting':True} user_log_id = Column("user_log_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=1) - user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=True, unique=None, default=None) + user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None) repository = Column("repository", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) action = Column("action", TEXT(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) @@ -33,14 +38,12 @@ class Repository(Base): __tablename__ = 'repositories' - repo_id = Column("repo_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=1) - repo_name = Column("repo_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) - user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=True, unique=None, default=None) + repo_name = Column("repo_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None, primary_key=True) + 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) - + description = Column("description", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) user = relation('User') - class Permission(Base): __tablename__ = 'permissions' __table_args__ = {'useexisting':True} diff -r 0d68a749db33 -r 0e5455fda8fd pylons_app/model/forms.py --- a/pylons_app/model/forms.py Sun Jun 06 23:35:21 2010 +0200 +++ b/pylons_app/model/forms.py Mon Jun 07 00:18:33 2010 +0200 @@ -25,8 +25,9 @@ from pylons import session from pylons.i18n.translation import _ from pylons_app.lib.auth import get_crypt_password +import pylons_app.lib.helpers as h from pylons_app.model import meta -from pylons_app.model.db import User +from pylons_app.model.db import User, Repository from sqlalchemy.exc import OperationalError from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound from webhelpers.pylonslib.secure_form import authentication_token @@ -93,6 +94,7 @@ auth_user.username = username auth_user.is_authenticated = True auth_user.is_admin = user.admin + auth_user.user_id = user.user_id session['hg_app_user'] = auth_user session.save() log.info('user %s is now authenticated', username) @@ -119,7 +121,30 @@ error_dict=self.e_dict_disable) - +class ValidRepoUser(formencode.validators.FancyValidator): + + def to_python(self, value, state): + sa = meta.Session + try: + self.user_db = sa.query(User).filter(User.username == value).one() + except Exception: + raise formencode.Invalid(_('This username is not valid'), + value, state) + return self.user_db.user_id + +def ValidRepoName(edit=False): + class _ValidRepoName(formencode.validators.FancyValidator): + + def to_python(self, value, state): + slug = h.repo_name_slug(value) + + sa = meta.Session + if sa.query(Repository).get(slug) and not edit: + raise formencode.Invalid(_('This repository already exists'), + value, state) + + return slug + return _ValidRepoName #=============================================================================== # FORMS #=============================================================================== @@ -163,3 +188,16 @@ email = Email(not_empty=True) return _UserForm + +def RepoForm(edit=False): + class _RepoForm(formencode.Schema): + allow_extra_fields = True + filter_extra_fields = True + repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit)) + description = UnicodeString(strip=True, min=3, not_empty=True) + private = StringBoolean(if_missing=False) + + if edit: + user = All(Int(not_empty=True), ValidRepoUser) + + return _RepoForm diff -r 0d68a749db33 -r 0e5455fda8fd pylons_app/model/hg_model.py --- a/pylons_app/model/hg_model.py Sun Jun 06 23:35:21 2010 +0200 +++ b/pylons_app/model/hg_model.py Mon Jun 07 00:18:33 2010 +0200 @@ -27,8 +27,9 @@ from beaker.cache import cache_region from mercurial import ui from mercurial.hgweb.hgwebdir_mod import findrepos -from pylons import app_globals as g from vcs.exceptions import RepositoryError, VCSError +from pylons_app.model.meta import Session +from pylons_app.model.db import Repository import logging import os import sys @@ -40,12 +41,19 @@ sys.stderr.write('You have to import vcs module') raise Exception('Unable to import vcs') +def _get_repos_cached_initial(app_globals): + """ + return cached dict with repos + """ + g = app_globals + return HgModel.repo_scan(g.paths[0][0], g.paths[0][1], g.baseui) @cache_region('long_term', 'cached_repo_list') def _get_repos_cached(): """ return cached dict with repos """ + from pylons import app_globals as g return HgModel.repo_scan(g.paths[0][0], g.paths[0][1], g.baseui) @cache_region('long_term', 'full_changelog') @@ -62,7 +70,6 @@ """ Constructor """ - pass @staticmethod def repo_scan(repos_prefix, repos_path, baseui): @@ -72,6 +79,7 @@ :param repos_path: path to directory it could take syntax with * or ** for deep recursive displaying repositories """ + sa = Session() def check_repo_dir(path): """ Checks the repository @@ -102,8 +110,13 @@ raise RepositoryError('Duplicate repository name %s found in' ' %s' % (name, path)) else: + repos_list[name] = MercurialRepository(path, baseui=baseui) repos_list[name].name = name + dbrepo = sa.query(Repository).get(name) + if dbrepo: + repos_list[name].description = dbrepo.description + repos_list[name].contact = dbrepo.user.full_contact except OSError: continue return repos_list diff -r 0d68a749db33 -r 0e5455fda8fd pylons_app/model/repo_model.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pylons_app/model/repo_model.py Mon Jun 07 00:18:33 2010 +0200 @@ -0,0 +1,106 @@ +#!/usr/bin/env python +# encoding: utf-8 +# model for handling repositories actions +# Copyright (C) 2009-2010 Marcin Kuzminski +# 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. + +""" +Created on Jun 5, 2010 +model for handling repositories actions +@author: marcink +""" +from pylons_app.model.meta import Session +from pylons_app.model.db import Repository +import shutil +import os +from datetime import datetime +from pylons_app.lib.utils import check_repo +from pylons import app_globals as g +import logging +log = logging.getLogger(__name__) + +class RepoModel(object): + + def __init__(self): + self.sa = Session() + + def get(self, id): + return self.sa.query(Repository).get(id) + + + def update(self, id, form_data): + try: + if id != form_data['repo_name']: + self.__rename_repo(id, form_data['repo_name']) + cur_repo = self.sa.query(Repository).get(id) + for k, v in form_data.items(): + if k == 'user': + cur_repo.user_id = v + else: + setattr(cur_repo, k, v) + + self.sa.add(cur_repo) + self.sa.commit() + except Exception as e: + log.error(e) + self.sa.rollback() + raise + + def create(self, form_data, cur_user): + try: + new_repo = Repository() + for k, v in form_data.items(): + setattr(new_repo, k, v) + + new_repo.user_id = cur_user.user_id + self.sa.add(new_repo) + self.sa.commit() + self.__create_repo(form_data['repo_name']) + except Exception as e: + log.error(e) + self.sa.rollback() + raise + + def delete(self, repo): + try: + self.sa.delete(repo) + self.sa.commit() + self.__delete_repo(repo.repo_name) + except Exception as e: + log.error(e) + self.sa.rollback() + raise + + def __create_repo(self, repo_name): + repo_path = os.path.join(g.base_path, repo_name) + if check_repo(repo_name, g.base_path): + log.info('creating repo %s in %s', repo_name, repo_path) + from vcs.backends.hg import MercurialRepository + MercurialRepository(repo_path, create=True) + + def __rename_repo(self, old, new): + log.info('renaming repoo from %s to %s', old, new) + old_path = os.path.join(g.base_path, old) + new_path = os.path.join(g.base_path, new) + shutil.move(old_path, new_path) + + def __delete_repo(self, name): + rm_path = os.path.join(g.base_path, name) + log.info("Removing %s", rm_path) + #disable hg + shutil.move(os.path.join(rm_path, '.hg'), os.path.join(rm_path, 'rm__.hg')) + #disable repo + shutil.move(rm_path, os.path.join(g.base_path, 'rm__%s-%s' % (datetime.today(), id))) diff -r 0d68a749db33 -r 0e5455fda8fd pylons_app/model/user_model.py --- a/pylons_app/model/user_model.py Sun Jun 06 23:35:21 2010 +0200 +++ b/pylons_app/model/user_model.py Mon Jun 07 00:18:33 2010 +0200 @@ -26,6 +26,8 @@ from pylons_app.model.db import User from pylons_app.model.meta import Session +import logging +log = logging.getLogger(__name__) class UserModel(object): @@ -43,7 +45,8 @@ self.sa.add(new_user) self.sa.commit() - except: + except Exception as e: + log.error(e) self.sa.rollback() raise @@ -59,6 +62,16 @@ self.sa.add(new_user) self.sa.commit() - except: + except Exception as e: + log.error(e) self.sa.rollback() raise + + def delete(self, id): + try: + self.sa.delete(self.sa.query(User).get(id)) + self.sa.commit() + except Exception as e: + log.error(e) + self.sa.rollback() + raise diff -r 0d68a749db33 -r 0e5455fda8fd pylons_app/templates/admin/repos/repo_add.html --- a/pylons_app/templates/admin/repos/repo_add.html Sun Jun 06 23:35:21 2010 +0200 +++ b/pylons_app/templates/admin/repos/repo_add.html Mon Jun 07 00:18:33 2010 +0200 @@ -20,15 +20,18 @@ - + + + + diff -r 0d68a749db33 -r 0e5455fda8fd pylons_app/templates/admin/repos/repo_edit.html --- a/pylons_app/templates/admin/repos/repo_edit.html Sun Jun 06 23:35:21 2010 +0200 +++ b/pylons_app/templates/admin/repos/repo_edit.html Mon Jun 07 00:18:33 2010 +0200 @@ -16,19 +16,27 @@ <%def name="main()">

${_('Repositories')} - ${_('edit')}

- ${h.form(url('repo', id=c.new_repo),method='put')} + ${h.form(url('repo', id=c.repo_info.repo_name),method='put')}
${_('Name')}${h.text('name',c.new_repo)}${h.text('repo_name',c.new_repo)}${self.get_form_error('repo_name')}
${_('Description')} ${h.textarea('description',cols=23,rows=5)}${self.get_form_error('description')}
${_('Private')} ${h.checkbox('private')}${self.get_form_error('private')}
- + + + + + + + + + diff -r 0d68a749db33 -r 0e5455fda8fd pylons_app/templates/summary/summary.html --- a/pylons_app/templates/summary/summary.html Sun Jun 06 23:35:21 2010 +0200 +++ b/pylons_app/templates/summary/summary.html Mon Jun 07 00:18:33 2010 +0200 @@ -70,7 +70,7 @@
${_('Name')}${h.text('name',c.new_repo)}${h.text('repo_name')}${self.get_form_error('repo_name')}
${_('Description')} ${h.textarea('description',cols=23,rows=5)}${self.get_form_error('description')}
${_('Private')} ${h.checkbox('private')}${self.get_form_error('private')}
${_('Owner')}${h.text('user')}${self.get_form_error('user')}
${cs.author|n,filters.person} r${cs.revision} - ${h.link_to(truncate(cs.message,60), + ${h.link_to(h.truncate(cs.message,60), h.url('changeset_home',repo_name=c.repo_name,revision=cs._short), title=cs.message)}