Changeset - b9442a8b5e02
[Not reviewed]
beta
0 3 0
Marcin Kuzminski - 15 years ago 2010-11-14 01:30:02
marcin@python-works.com
fixed admin settings form resubmition bug.
removed obsolete whoosh index location. Some small templates fixes
3 files changed with 10 insertions and 14 deletions:
0 comments (0 inline, 0 general)
rhodecode/controllers/admin/settings.py
Show inline comments
 
#!/usr/bin/env python
 
# encoding: utf-8
 
# settings controller for pylons
 
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
 
#
 
# 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 July 14, 2010
 
settings controller for pylons
 
@author: marcink
 
"""
 
from formencode import htmlfill
 
from pylons import request, session, tmpl_context as c, url, app_globals as g, \
 
    config
 
from pylons.controllers.util import abort, redirect
 
from pylons.i18n.translation import _
 
from rhodecode.lib import helpers as h
 
from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
 
    HasPermissionAnyDecorator
 
from rhodecode.lib.base import BaseController, render
 
from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
 
    set_rhodecode_config, get_hg_settings, get_hg_ui_settings
 
from rhodecode.model.db import RhodeCodeSettings, RhodeCodeUi, Repository
 
from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
 
    ApplicationUiSettingsForm
 
from rhodecode.model.hg import HgModel
 
from rhodecode.model.user import UserModel
 
from rhodecode.lib.celerylib import tasks, run_task
 
from sqlalchemy import func
 
import formencode
 
import logging
 
import traceback
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class SettingsController(BaseController):
 
    """REST Controller styled on the Atom Publishing Protocol"""
 
    # To properly map this controller, ensure your config/routing.py
 
    # file has a resource setup:
 
    #     map.resource('setting', 'settings', controller='admin/settings', 
 
    #         path_prefix='/admin', name_prefix='admin_')
 

	
 

	
 
    @LoginRequired()
 
    def __before__(self):
 
        c.admin_user = session.get('admin_user')
 
        c.admin_username = session.get('admin_username')
 
        super(SettingsController, self).__before__()
 

	
 

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

	
 
        defaults = get_hg_settings()
 
        defaults.update(get_hg_ui_settings())
 
        return htmlfill.render(
 
            render('admin/settings/settings.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False
 
        )
 

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

	
 
    @HasPermissionAllDecorator('hg.admin')
 
    def new(self, format='html'):
 
        """GET /admin/settings/new: Form to create a new item"""
 
        # url('admin_new_setting')
 

	
 
    @HasPermissionAllDecorator('hg.admin')
 
    def update(self, setting_id):
 
        """PUT /admin/settings/setting_id: Update an existing item"""
 
        # Forms posted to this method should contain a hidden field:
 
        #    <input type="hidden" name="_method" value="PUT" />
 
        # Or using helpers:
 
        #    h.form(url('admin_setting', setting_id=ID),
 
        #           method='put')
 
        # url('admin_setting', setting_id=ID)
 
        if setting_id == 'mapping':
 
            rm_obsolete = request.POST.get('destroy', False)
 
            log.debug('Rescanning directories with destroy=%s', rm_obsolete)
 

	
 
            initial = HgModel().repo_scan(g.paths[0][1], g.baseui)
 
            for repo_name in initial.keys():
 
                invalidate_cache('get_repo_cached_%s' % repo_name)
 

	
 
            repo2db_mapper(initial, rm_obsolete)
 

	
 
            h.flash(_('Repositories successfully rescanned'), category='success')
 

	
 
        if setting_id == 'whoosh':
 
            repo_location = get_hg_ui_settings()['paths_root_path']
 
            full_index = request.POST.get('full_index', False)
 
            task = run_task(tasks.whoosh_index, repo_location, full_index)
 

	
 
            h.flash(_('Whoosh reindex task scheduled'), category='success')
 
        if setting_id == 'global':
 

	
 
            application_form = ApplicationSettingsForm()()
 
            try:
 
                form_result = application_form.to_python(dict(request.POST))
 

	
 
                try:
 
                    hgsettings1 = self.sa.query(RhodeCodeSettings)\
 
                        .filter(RhodeCodeSettings.app_settings_name \
 
                                == 'title').one()
 

	
 
                    hgsettings1.app_settings_value = form_result['rhodecode_title']
 

	
 
                    hgsettings2 = self.sa.query(RhodeCodeSettings)\
 
                        .filter(RhodeCodeSettings.app_settings_name \
 
                                == 'realm').one()
 

	
 
                    hgsettings2.app_settings_value = form_result['rhodecode_realm']
 

	
 

	
 
                    self.sa.add(hgsettings1)
 
                    self.sa.add(hgsettings2)
 
                    self.sa.commit()
 
                    set_rhodecode_config(config)
 
                    h.flash(_('Updated application settings'),
 
                            category='success')
 

	
 
                except:
 
                    log.error(traceback.format_exc())
 
                    h.flash(_('error occurred during updating application settings'),
 
                            category='error')
 

	
 
                    self.sa.rollback()
 

	
 

	
 
            except formencode.Invalid, errors:
 
                return htmlfill.render(
 
                     render('admin/settings/settings.html'),
 
                     defaults=errors.value,
 
                     errors=errors.error_dict or {},
 
                     prefix_error=False,
 
                     encoding="UTF-8")
 

	
 
        if setting_id == 'mercurial':
 
            application_form = ApplicationUiSettingsForm()()
 
            try:
 
                form_result = application_form.to_python(dict(request.POST))
 

	
 
                try:
 

	
 
                    hgsettings1 = self.sa.query(RhodeCodeUi)\
 
                    .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
 
                    hgsettings1.ui_value = form_result['web_push_ssl']
 

	
 
                    hgsettings2 = self.sa.query(RhodeCodeUi)\
 
                    .filter(RhodeCodeUi.ui_key == '/').one()
 
                    hgsettings2.ui_value = form_result['paths_root_path']
 

	
 

	
 
                    #HOOKS
 
                    hgsettings3 = self.sa.query(RhodeCodeUi)\
 
                    .filter(RhodeCodeUi.ui_key == 'changegroup.update').one()
 
                    hgsettings3.ui_active = bool(form_result['hooks_changegroup_update'])
 

	
 
                    hgsettings4 = self.sa.query(RhodeCodeUi)\
 
                    .filter(RhodeCodeUi.ui_key == 'changegroup.repo_size').one()
 
                    hgsettings4.ui_active = bool(form_result['hooks_changegroup_repo_size'])
 

	
 
                    hgsettings5 = self.sa.query(RhodeCodeUi)\
 
                    .filter(RhodeCodeUi.ui_key == 'pretxnchangegroup.push_logger').one()
 
                    hgsettings5.ui_active = bool(form_result['hooks_pretxnchangegroup_push_logger'])
 

	
 
                    hgsettings6 = self.sa.query(RhodeCodeUi)\
 
                    .filter(RhodeCodeUi.ui_key == 'preoutgoing.pull_logger').one()
 
                    hgsettings6.ui_active = bool(form_result['hooks_preoutgoing_pull_logger'])
 

	
 

	
 
                    self.sa.add(hgsettings1)
 
                    self.sa.add(hgsettings2)
 
                    self.sa.add(hgsettings3)
 
                    self.sa.add(hgsettings4)
 
                    self.sa.add(hgsettings5)
 
                    self.sa.add(hgsettings6)
 
                    self.sa.commit()
 

	
 
                    h.flash(_('Updated mercurial settings'),
 
                            category='success')
 

	
 
                except:
 
                    log.error(traceback.format_exc())
 
                    h.flash(_('error occurred during updating application settings'),
 
                            category='error')
 

	
 
                    self.sa.rollback()
 

	
 

	
 
            except formencode.Invalid, errors:
 
                return htmlfill.render(
 
                     render('admin/settings/settings.html'),
 
                     defaults=errors.value,
 
                     errors=errors.error_dict or {},
 
                     prefix_error=False,
 
                     encoding="UTF-8")
 

	
 

	
 

	
 
        return redirect(url('admin_settings'))
 

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

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

	
 
    @HasPermissionAllDecorator('hg.admin')
 
    def edit(self, setting_id, format='html'):
 
        """GET /admin/settings/setting_id/edit: Form to edit an existing item"""
 
        # url('admin_edit_setting', setting_id=ID)
 

	
 

	
 
    def my_account(self):
 
        """
 
        GET /_admin/my_account Displays info about my account 
 
        """
 

	
 
        # url('admin_settings_my_account')
 
        c.user = UserModel(self.sa).get(c.rhodecode_user.user_id, cache=False)
 
        all_repos = self.sa.query(Repository)\
 
            .filter(Repository.user_id == c.user.user_id)\
 
            .order_by(func.lower(Repository.repo_name))\
 
            .all()
 
        c.user_repos = HgModel().get_repos(all_repos)
 

	
 
        if c.user.username == 'default':
 
            h.flash(_("You can't edit this user since it's"
 
              " crucial for entire application"), category='warning')
 
            return redirect(url('users'))
 

	
 
        defaults = c.user.__dict__
 
        return htmlfill.render(
 
            render('admin/users/user_edit_my_account.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False
 
        )
 

	
 
    def my_account_update(self):
 
        """PUT /_admin/my_account_update: Update an existing item"""
 
        # Forms posted to this method should contain a hidden field:
 
        #    <input type="hidden" name="_method" value="PUT" />
 
        # Or using helpers:
 
        #    h.form(url('admin_settings_my_account_update'),
 
        #           method='put')
 
        # url('admin_settings_my_account_update', id=ID)
 
        user_model = UserModel()
 
        uid = c.rhodecode_user.user_id
 
        _form = UserForm(edit=True, old_data={'user_id':uid,
 
                                              'email':c.rhodecode_user.email})()
 
        form_result = {}
 
        try:
 
            form_result = _form.to_python(dict(request.POST))
 
            user_model.update_my_account(uid, form_result)
 
            h.flash(_('Your account was updated succesfully'),
 
                    category='success')
 

	
 
        except formencode.Invalid, errors:
 
            c.user = user_model.get(c.rhodecode_user.user_id, cache=False)
 
            c.user_repos = []
 
            for repo in c.cached_repo_list.values():
 
                if repo.dbrepo.user.username == c.user.username:
 
                    c.user_repos.append(repo)
 
            c.user = UserModel(self.sa).get(c.rhodecode_user.user_id, cache=False)
 
            all_repos = self.sa.query(Repository)\
 
                .filter(Repository.user_id == c.user.user_id)\
 
                .order_by(func.lower(Repository.repo_name))\
 
                .all()
 
            c.user_repos = HgModel().get_repos(all_repos)
 

	
 
            return htmlfill.render(
 
                render('admin/users/user_edit_my_account.html'),
 
                defaults=errors.value,
 
                errors=errors.error_dict or {},
 
                prefix_error=False,
 
                encoding="UTF-8")
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('error occured during update of user %s') \
 
                    % form_result.get('username'), category='error')
 

	
 
        return redirect(url('my_account'))
 

	
 
    @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
 
    def create_repository(self):
 
        """GET /_admin/create_repository: Form to create a new item"""
 
        new_repo = request.GET.get('repo', '')
 
        c.new_repo = h.repo_name_slug(new_repo)
 

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

	
rhodecode/lib/indexers/__init__.py
Show inline comments
 
import os
 
import sys
 
from os.path import dirname as dn, join as jn
 

	
 
#to get the rhodecode import
 
sys.path.append(dn(dn(dn(os.path.realpath(__file__)))))
 

	
 
from rhodecode.config.environment import load_environment
 
from rhodecode.model.hg import HgModel
 
from shutil import rmtree
 
from webhelpers.html.builder import escape
 
from vcs.utils.lazy import LazyProperty
 

	
 
from whoosh.analysis import RegexTokenizer, LowercaseFilter, StopFilter
 
from whoosh.fields import TEXT, ID, STORED, Schema, FieldType
 
from whoosh.index import create_in, open_dir
 
from whoosh.formats import Characters
 
from whoosh.highlight import highlight, SimpleFragmenter, HtmlFormatter
 

	
 
import traceback
 

	
 

	
 
#LOCATION WE KEEP THE INDEX
 
IDX_LOCATION = jn(dn(dn(dn(dn(os.path.abspath(__file__))))), 'data', 'index')
 

	
 
#EXTENSIONS WE WANT TO INDEX CONTENT OFF
 
INDEX_EXTENSIONS = ['action', 'adp', 'ashx', 'asmx', 'aspx', 'asx', 'axd', 'c',
 
                    'cfg', 'cfm', 'cpp', 'cs', 'css', 'diff', 'do', 'el', 'erl',
 
                    'h', 'htm', 'html', 'ini', 'java', 'js', 'jsp', 'jspx', 'lisp',
 
                    'lua', 'm', 'mako', 'ml', 'pas', 'patch', 'php', 'php3',
 
                    'php4', 'phtml', 'pm', 'py', 'rb', 'rst', 's', 'sh', 'sql',
 
                    'tpl', 'txt', 'vim', 'wss', 'xhtml', 'xml', 'xsl', 'xslt',
 
                    'yaws']
 

	
 
#CUSTOM ANALYZER wordsplit + lowercase filter
 
ANALYZER = RegexTokenizer(expression=r"\w+") | LowercaseFilter()
 

	
 

	
 
#INDEX SCHEMA DEFINITION
 
SCHEMA = Schema(owner=TEXT(),
 
                repository=TEXT(stored=True),
 
                path=TEXT(stored=True),
 
                content=FieldType(format=Characters(ANALYZER),
 
                             scorable=True, stored=True),
 
                modtime=STORED(), extension=TEXT(stored=True))
 

	
 

	
 
IDX_NAME = 'HG_INDEX'
 
FORMATTER = HtmlFormatter('span', between='\n<span class="break">...</span>\n')
 
FRAGMENTER = SimpleFragmenter(200)
 

	
 
from paste.script import command
 
import ConfigParser
 

	
 
class MakeIndex(command.Command):
 

	
 
    max_args = 1
 
    min_args = 1
 

	
 
    usage = "CONFIG_FILE"
 
    summary = "Creates index for full text search given configuration file"
 
    group_name = "RhodeCode"
 
    takes_config_file = -1
 
    parser = command.Command.standard_parser(verbose=True)
 
    parser.add_option('--repo-location',
 
                      action='store',
 
                      dest='repo_location',
 
                      help="Specifies repositories location to index REQUIRED",
 
                      )
 
    parser.add_option('-f',
 
                      action='store_true',
 
                      dest='full_index',
 
                      help="Specifies that index should be made full i.e"
 
                            " destroy old and build from scratch",
 
                      default=False)
 
    def command(self):
 
        config_name = self.args[0]
 
        p = config_name.split('/')
 
        root = '.' if len(p) == 1 else '/'.join(p[:-1])
 
        config = ConfigParser.ConfigParser({'here':root})
 
        config.read(config_name)
 

	
 
        index_location = dict(config.items('app:main'))['index_dir']
 
        repo_location = self.options.repo_location
 

	
 
        #======================================================================
 
        # WHOOSH DAEMON
 
        #======================================================================
 
        from rhodecode.lib.pidlock import LockHeld, DaemonLock
 
        from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
 
        try:
 
            l = DaemonLock()
 
            WhooshIndexingDaemon(index_location=index_location,
 
                                 repo_location=repo_location)\
 
                .run(full_index=self.options.full_index)
 
            l.release()
 
        except LockHeld:
 
            sys.exit(1)
 

	
 

	
 
class ResultWrapper(object):
 
    def __init__(self, search_type, searcher, matcher, highlight_items):
 
        self.search_type = search_type
 
        self.searcher = searcher
 
        self.matcher = matcher
 
        self.highlight_items = highlight_items
 
        self.fragment_size = 200 / 2
 

	
 
    @LazyProperty
 
    def doc_ids(self):
 
        docs_id = []
 
        while self.matcher.is_active():
 
            docnum = self.matcher.id()
 
            chunks = [offsets for offsets in self.get_chunks()]
 
            docs_id.append([docnum, chunks])
 
            self.matcher.next()
 
        return docs_id
 

	
 
    def __str__(self):
 
        return '<%s at %s>' % (self.__class__.__name__, len(self.doc_ids))
 

	
 
    def __repr__(self):
 
        return self.__str__()
 

	
 
    def __len__(self):
 
        return len(self.doc_ids)
 

	
 
    def __iter__(self):
 
        """
 
        Allows Iteration over results,and lazy generate content
 

	
 
        *Requires* implementation of ``__getitem__`` method.
 
        """
 
        for docid in self.doc_ids:
 
            yield self.get_full_content(docid)
 

	
 
    def __getslice__(self, i, j):
 
        """
 
        Slicing of resultWrapper
 
        """
 
        slice = []
 
        for docid in self.doc_ids[i:j]:
 
            slice.append(self.get_full_content(docid))
 
        return slice
 

	
 

	
 
    def get_full_content(self, docid):
 
        res = self.searcher.stored_fields(docid[0])
 
        f_path = res['path'][res['path'].find(res['repository']) \
 
                             + len(res['repository']):].lstrip('/')
 

	
 
        content_short = self.get_short_content(res, docid[1])
 
        res.update({'content_short':content_short,
 
                    'content_short_hl':self.highlight(content_short),
 
                    'f_path':f_path})
 

	
 
        return res
 

	
 
    def get_short_content(self, res, chunks):
 

	
 
        return ''.join([res['content'][chunk[0]:chunk[1]] for chunk in chunks])
 

	
 
    def get_chunks(self):
 
        """
 
        Smart function that implements chunking the content
 
        but not overlap chunks so it doesn't highlight the same
 
        close occurrences twice.
 
        @param matcher:
 
        @param size:
 
        """
 
        memory = [(0, 0)]
 
        for span in self.matcher.spans():
 
            start = span.startchar or 0
 
            end = span.endchar or 0
 
            start_offseted = max(0, start - self.fragment_size)
 
            end_offseted = end + self.fragment_size
 

	
 
            if start_offseted < memory[-1][1]:
 
                start_offseted = memory[-1][1]
 
            memory.append((start_offseted, end_offseted,))
 
            yield (start_offseted, end_offseted,)
 

	
 
    def highlight(self, content, top=5):
 
        if self.search_type != 'content':
 
            return ''
 
        hl = highlight(escape(content),
 
                 self.highlight_items,
 
                 analyzer=ANALYZER,
 
                 fragmenter=FRAGMENTER,
 
                 formatter=FORMATTER,
 
                 top=top)
 
        return hl
rhodecode/templates/changelog/changelog.html
Show inline comments
 
## -*- coding: utf-8 -*-
 

	
 
<%inherit file="/base/base.html"/>
 

	
 
<%def name="title()">
 
${c.repo_name} ${_('Changelog')} - ${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;
 
    ${_('Changelog')} - ${_('showing ')} ${c.size if c.size <= c.total_cs else c.total_cs} ${_('out of')} ${c.total_cs} ${_('revisions')}  
 
</%def>
 

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

	
 
<%def name="main()">
 
<div class="box">
 
    <!-- box / title -->
 
    <div class="title">
 
        ${self.breadcrumbs()}
 
    </div>
 
    <div class="table">
 
		% if c.pagination:
 
			<div id="graph">
 
				<div id="graph_nodes">
 
					<canvas id="graph_canvas"></canvas>
 
				</div>
 
				<div id="graph_content">
 
					<div class="container_header">
 
						
 
        ${h.form(h.url.current(),method='get')}
 
        <div class="info_box">
 
          <span>${_('Show')}:</span>
 
          ${h.text('size',size=1,value=c.size)}
 
          <span>${_('revisions')}</span>
 
          ${h.submit('set',_('set'))}
 
        </div>
 
        ${h.end_form()}
 
						
 
					</div>
 
				%for cnt,cs in enumerate(c.pagination):
 
					<div id="chg_${cnt+1}" class="container">
 
						<div class="left">
 
							<div class="date">${_('commit')} ${cs.revision}: ${h.short_id(cs.raw_id)}@${cs.date}</div>
 
							<div class="author">
 
								<div class="gravatar">
 
									<img alt="gravatar" src="${h.gravatar_url(h.email(cs.author),20)}"/>
 
								</div>
 
								<span>${h.person(cs.author)}</span><br/>
 
								<span><a href="mailto:${h.email_or_none(cs.author)}">${h.email_or_none(cs.author)}</a></span><br/>
 
							</div>
 
							<div class="message">${h.link_to(h.wrap_paragraphs(cs.message),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</div>
 
						</div>	
 
						<div class="right">
 
									<div class="changes">
 
										<span class="removed" title="${_('removed')}: ${' | '.join([x.name for x in cs.removed])}">
 
										${len(cs.removed)}</span>
 
										<span class="changed" title="${_('changed')}: ${' | '.join([x.name for x in cs.changed])}">
 
										${len(cs.changed)}</span>
 
										<span class="added" title="${_('added')}: ${' | '.join([x.name for x in cs.added])}">
 
										${len(cs.added)}</span>
 
										<span class="removed" title="${_('removed')}: ${' | '.join([x.name for x in cs.removed])}">${len(cs.removed)}</span>
 
										<span class="changed" title="${_('changed')}: ${' | '.join([x.name for x in cs.changed])}">${len(cs.changed)}</span>
 
										<span class="added" title="${_('added')}: ${' | '.join([x.name for x in cs.added])}">${len(cs.added)}</span>
 
									</div>					
 
										%if len(cs.parents)>1:
 
										<div class="merge">
 
											${_('merge')}<img alt="merge" src="/images/icons/arrow_join.png"/>
 
										</div>
 
										%endif
 
								   %if cs.parents:							
 
									%for p_cs in reversed(cs.parents):
 
										<div class="parent">${_('Parent')} ${p_cs.revision}: ${h.link_to(h.short_id(p_cs.raw_id),
 
											h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.raw_id),title=p_cs.message)}
 
										</div>
 
									%endfor
 
								   %else:	
 
                                        <div class="parent">${_('No parents')}</div>   
 
                                   %endif  
 
                    									
 
								<span class="logtags">
 
									<span class="branchtag" title="${'%s %s' % (_('branch'),cs.branch)}">
 
									${h.link_to(cs.branch,h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}</span>
 
									%for tag in cs.tags:
 
										<span class="tagtag"  title="${'%s %s' % (_('tag'),tag)}">
 
										${h.link_to(tag,h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}</span>
 
									%endfor
 
								</span>																	
 
						</div>				
 
					</div>
 
					
 
				%endfor
 
				<div class="pagination-wh pagination-left">
 
					${c.pagination.pager('$link_previous ~2~ $link_next')}
 
				</div>			
 
				</div>
 
			</div>
 
			
 
			<script type="text/javascript" src="/js/graph.js"></script>
 
			<script type="text/javascript">
 
				YAHOO.util.Event.onDOMReady(function(){
 
					function set_canvas() {
 
						var c = document.getElementById('graph_nodes');
 
						var t = document.getElementById('graph_content');
 
						canvas = document.getElementById('graph_canvas');
 
						var div_h = t.clientHeight;
 
						c.style.height=div_h+'px';
 
						canvas.setAttribute('height',div_h);
 
						canvas.setAttribute('width',160);
 
					};
 
					set_canvas();
 
					var jsdata = ${c.jsdata|n};
 
					var r = new BranchRenderer();
 
					r.render(jsdata); 
 
				});
 
			</script>
 
		%else:
 
			${_('There are no changes yet')}
 
		%endif  
 
    </div>
 
</div>    
 
</%def>
 
\ No newline at end of file
0 comments (0 inline, 0 general)