@@ -75,53 +75,55 @@ def make_map(config):
function=check_repo))
#ajax delete repo perm user
m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*}",
action="delete_perm_user", conditions=dict(method=["DELETE"],
#ajax delete repo perm users_group
m.connect('delete_repo_users_group', "/repos_delete_users_group/{repo_name:.*}",
action="delete_perm_users_group", conditions=dict(method=["DELETE"],
#settings actions
m.connect('repo_stats', "/repos_stats/{repo_name:.*}",
action="repo_stats", conditions=dict(method=["DELETE"],
m.connect('repo_cache', "/repos_cache/{repo_name:.*}",
action="repo_cache", conditions=dict(method=["DELETE"],
m.connect('repo_public_journal', "/repos_public_journal/{repo_name:.*}",
action="repo_public_journal", conditions=dict(method=["PUT"],
m.connect('repo_pull', "/repo_pull/{repo_name:.*}",
action="repo_pull", conditions=dict(method=["PUT"],
#ADMIN REPOS GROUP REST ROUTES
routes_map.resource('repos_group', 'repos_groups', controller='admin/repos_groups', path_prefix='/_admin')
#ADMIN USER REST ROUTES
routes_map.resource('user', 'users', controller='admin/users', path_prefix='/_admin')
#ADMIN USERS REST ROUTES
routes_map.resource('users_group', 'users_groups', controller='admin/users_groups', path_prefix='/_admin')
#ADMIN GROUP REST ROUTES
routes_map.resource('group', 'groups', controller='admin/groups', path_prefix='/_admin')
#ADMIN PERMISSIONS REST ROUTES
routes_map.resource('permission', 'permissions', controller='admin/permissions', path_prefix='/_admin')
##ADMIN LDAP SETTINGS
routes_map.connect('ldap_settings', '/_admin/ldap', controller='admin/ldap_settings',
action='ldap_settings', conditions=dict(method=["POST"]))
routes_map.connect('ldap_home', '/_admin/ldap', controller='admin/ldap_settings',)
#ADMIN SETTINGS REST ROUTES
with routes_map.submapper(path_prefix='/_admin', controller='admin/settings') as m:
m.connect("admin_settings", "/settings",
action="create", conditions=dict(method=["POST"]))
action="index", conditions=dict(method=["GET"]))
m.connect("formatted_admin_settings", "/settings.{format}",
m.connect("admin_new_setting", "/settings/new",
action="new", conditions=dict(method=["GET"]))
new file 100644
import logging
from pylons import request, response, session, tmpl_context as c, url
from pylons.controllers.util import abort, redirect
from rhodecode.lib.base import BaseController, render
log = logging.getLogger(__name__)
class ReposGroupsController(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('repos_group', 'repos_groups')
def index(self, format='html'):
"""GET /repos_groups: All items in the collection"""
# url('repos_groups')
def create(self):
"""POST /repos_groups: Create a new item"""
def new(self, format='html'):
"""GET /repos_groups/new: Form to create a new item"""
# url('new_repos_group')
def update(self, id):
"""PUT /repos_groups/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('repos_group', id=ID),
# method='put')
# url('repos_group', id=ID)
def delete(self, id):
"""DELETE /repos_groups/id: Delete an existing item"""
# <input type="hidden" name="_method" value="DELETE" />
# method='delete')
def show(self, id, format='html'):
"""GET /repos_groups/id: Show a specific item"""
def edit(self, id, format='html'):
"""GET /repos_groups/id/edit: Form to edit an existing item"""
# url('edit_repos_group', id=ID)
@@ -14,156 +14,158 @@
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; version 2
# of the License or (at your opinion) any later version of the license.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
import calendar
from time import mktime
from datetime import datetime, timedelta, date
from vcs.exceptions import ChangesetError
from pylons import tmpl_context as c, request, url
from pylons.i18n.translation import _
from rhodecode.model.db import Statistics
from rhodecode.model.db import Statistics, Repository
from rhodecode.model.repo import RepoModel
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
from rhodecode.lib.base import BaseRepoController, render
from rhodecode.lib.utils import OrderedDict, EmptyChangeset
from rhodecode.lib.celerylib import run_task
from rhodecode.lib.celerylib.tasks import get_commits_stats
from rhodecode.lib.helpers import RepoPage
try:
import json
except ImportError:
#python 2.5 compatibility
import simplejson as json
class SummaryController(BaseRepoController):
@LoginRequired()
@HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
'repository.admin')
def __before__(self):
super(SummaryController, self).__before__()
def index(self):
c.repo, dbrepo = self.scm_model.get(c.repo_name)
c.dbrepo = dbrepo
def index(self, repo_name):
e = request.environ
c.dbrepo = dbrepo = Repository.by_repo_name(repo_name)
c.following = self.scm_model.is_following_repo(c.repo_name,
self.rhodecode_user.user_id)
c.following = self.scm_model.is_following_repo(repo_name,
def url_generator(**kw):
return url('shortlog_home', repo_name=c.repo_name, **kw)
return url('shortlog_home', repo_name=repo_name, **kw)
c.repo_changesets = RepoPage(c.repo, page=1, items_per_page=10,
c.repo_changesets = RepoPage(c.rhodecode_repo, page=1, items_per_page=10,
url=url_generator)
if self.rhodecode_user.username == 'default':
#for default(anonymous) user we don't need to pass credentials
username = ''
password = ''
else:
username = str(self.rhodecode_user.username)
password = '@'
uri = u'%(protocol)s://%(user)s%(password)s%(host)s%(prefix)s/%(repo_name)s' % {
'protocol': e.get('wsgi.url_scheme'),
'user':username,
'password':password,
'host':e.get('HTTP_HOST'),
'prefix':e.get('SCRIPT_NAME'),
'repo_name':c.repo_name, }
'repo_name':repo_name, }
c.clone_repo_url = uri
c.repo_tags = OrderedDict()
for name, hash in c.repo.tags.items()[:10]:
for name, hash in c.rhodecode_repo.tags.items()[:10]:
c.repo_tags[name] = c.repo.get_changeset(hash)
c.repo_tags[name] = c.rhodecode_repo.get_changeset(hash)
except ChangesetError:
c.repo_tags[name] = EmptyChangeset(hash)
c.repo_branches = OrderedDict()
for name, hash in c.repo.branches.items()[:10]:
for name, hash in c.rhodecode_repo.branches.items()[:10]:
c.repo_branches[name] = c.repo.get_changeset(hash)
c.repo_branches[name] = c.rhodecode_repo.get_changeset(hash)
c.repo_branches[name] = EmptyChangeset(hash)
td = date.today() + timedelta(days=1)
td_1m = td - timedelta(days=calendar.mdays[td.month])
td_1y = td - timedelta(days=365)
ts_min_m = mktime(td_1m.timetuple())
ts_min_y = mktime(td_1y.timetuple())
ts_max_y = mktime(td.timetuple())
if dbrepo.enable_statistics:
c.no_data_msg = _('No data loaded yet')
run_task(get_commits_stats, c.repo.name, ts_min_y, ts_max_y)
run_task(get_commits_stats, c.dbrepo.repo_name, ts_min_y, ts_max_y)
c.no_data_msg = _('Statistics are disabled for this repository')
c.ts_min = ts_min_m
c.ts_max = ts_max_y
stats = self.sa.query(Statistics)\
.filter(Statistics.repository == dbrepo)\
.scalar()
if stats and stats.languages:
c.no_data = False is dbrepo.enable_statistics
lang_stats = json.loads(stats.languages)
c.commit_data = stats.commit_activity
c.overview_data = stats.commit_activity_combined
c.trending_languages = json.dumps(OrderedDict(
sorted(lang_stats.items(), reverse=True,
key=lambda k: k[1])[:10]
)
c.commit_data = json.dumps({})
c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 10] ])
c.trending_languages = json.dumps({})
c.no_data = True
c.enable_downloads = dbrepo.enable_downloads
if c.enable_downloads:
c.download_options = self._get_download_links(c.repo)
c.download_options = self._get_download_links(c.rhodecode_repo)
return render('summary/summary.html')
def _get_download_links(self, repo):
download_l = []
branches_group = ([], _("Branches"))
tags_group = ([], _("Tags"))
for name, chs in c.rhodecode_repo.branches.items():
#chs = chs.split(':')[-1]
branches_group[0].append((chs, name),)
download_l.append(branches_group)
for name, chs in c.rhodecode_repo.tags.items():
tags_group[0].append((chs, name),)
download_l.append(tags_group)
return download_l
@@ -272,102 +272,105 @@ class CodeHtmlFormatter(HtmlFormatter):
# some configurations seem to mess up the formatting...
if nocls:
yield 0, ('<table class="%stable">' % self.cssclass +
'<tr><td><div class="linenodiv" '
'style="background-color: #f0f0f0; padding-right: 10px">'
'<pre style="line-height: 125%">' +
ls + '</pre></div></td><td class="code">')
'<tr><td class="linenos"><div class="linenodiv"><pre>' +
yield 0, dummyoutfile.getvalue()
yield 0, '</td></tr></table>'
def pygmentize(filenode, **kwargs):
"""pygmentize function using pygments
:param filenode:
"""
return literal(code_highlight(filenode.content,
filenode.lexer, CodeHtmlFormatter(**kwargs)))
def pygmentize_annotation(filenode, **kwargs):
def pygmentize_annotation(repo_name, filenode, **kwargs):
"""pygmentize function for annotation
color_dict = {}
def gen_color(n=10000):
"""generator for getting n of evenly distributed colors using
hsv color and golden ratio. It always return same order of colors
:returns: RGB tuple
import colorsys
golden_ratio = 0.618033988749895
h = 0.22717784590367374
for c in xrange(n):
h += golden_ratio
h %= 1
HSV_tuple = [h, 0.95, 0.95]
RGB_tuple = colorsys.hsv_to_rgb(*HSV_tuple)
yield map(lambda x:str(int(x * 256)), RGB_tuple)
cgenerator = gen_color()
def get_color_string(cs):
if color_dict.has_key(cs):
col = color_dict[cs]
col = color_dict[cs] = cgenerator.next()
return "color: rgb(%s)! important;" % (', '.join(col))
def url_func(changeset):
tooltip_html = "<div style='font-size:0.8em'><b>Author:</b>" + \
" %s<br/><b>Date:</b> %s</b><br/><b>Message:</b> %s<br/></div>"
def url_func(repo_name):
def _url_func(changeset):
tooltip_html = tooltip_html % (changeset.author,
changeset.date,
tooltip(changeset.message))
lnk_format = '%5s:%s' % ('r%s' % changeset.revision,
short_id(changeset.raw_id))
uri = link_to(
lnk_format,
url('changeset_home', repo_name=changeset.repository.name,
revision=changeset.raw_id),
style=get_color_string(changeset.raw_id),
class_='tooltip',
title=tooltip_html
url('changeset_home', repo_name=repo_name,
uri += '\n'
return uri
return literal(annotate_highlight(filenode, url_func, **kwargs))
return _url_func
return literal(annotate_highlight(filenode, url_func(repo_name), **kwargs))
def get_changeset_safe(repo, rev):
from vcs.backends.base import BaseRepository
from vcs.exceptions import RepositoryError
if not isinstance(repo, BaseRepository):
raise Exception('You must pass an Repository '
'object as first argument got %s', type(repo))
cs = repo.get_changeset(rev)
except RepositoryError:
from rhodecode.lib.utils import EmptyChangeset
cs = EmptyChangeset()
return cs
def is_following_repo(repo_name, user_id):
from rhodecode.model.scm import ScmModel
return ScmModel().is_following_repo(repo_name, user_id)
flash = _Flash()
#==============================================================================
@@ -669,29 +672,29 @@ class RepoPage(Page):
# This is a subclass of the 'list' type. Initialise the list now.
list.__init__(self, self.items)
def changed_tooltip(nodes):
if nodes:
pref = ': <br/> '
suf = ''
if len(nodes) > 30:
suf = '<br/>' + _(' and %s more') % (len(nodes) - 30)
return literal(pref + '<br/> '.join([x.path.decode('utf-8', 'replace') for x in nodes[:30]]) + suf)
return ': ' + _('No Files')
def repo_link(groups_and_repos):
groups, repo_name = groups_and_repos
if not groups:
return repo_name
def make_link(group):
return link_to(group.group_name, url('/', group.group_id))
return link_to(group.group_name, url('repos_group', id=group.group_id))
return literal(' » '.join(map(make_link, groups)) + \
" » " + repo_name)
@@ -70,172 +70,172 @@ log.addHandler(ch)
class WhooshIndexingDaemon(object):
Deamon for atomic jobs
def __init__(self, indexname='HG_INDEX', index_location=None,
repo_location=None, sa=None, repo_list=None):
self.indexname = indexname
self.index_location = index_location
if not index_location:
raise Exception('You have to provide index location')
self.repo_location = repo_location
if not repo_location:
raise Exception('You have to provide repositories location')
self.repo_paths = ScmModel(sa).repo_scan(self.repo_location)
if repo_list:
filtered_repo_paths = {}
for repo_name, repo in self.repo_paths.items():
if repo_name in repo_list:
filtered_repo_paths[repo.name] = repo
filtered_repo_paths[repo_name] = repo
self.repo_paths = filtered_repo_paths
self.initial = False
if not os.path.isdir(self.index_location):
os.makedirs(self.index_location)
log.info('Cannot run incremental index since it does not'
' yet exist running full build')
self.initial = True
def get_paths(self, repo):
"""recursive walk in root dir and return a set of all path in that dir
based on repository walk function
index_paths_ = set()
tip = repo.get_changeset('tip')
for topnode, dirs, files in tip.walk('/'):
for f in files:
index_paths_.add(jn(repo.path, f.path))
for dir in dirs:
except RepositoryError, e:
log.debug(traceback.format_exc())
pass
return index_paths_
def get_node(self, repo, path):
n_path = path[len(repo.path) + 1:]
node = repo.get_changeset().get_node(n_path)
return node
def get_node_mtime(self, node):
return mktime(node.last_changeset.date.timetuple())
def add_doc(self, writer, path, repo):
def add_doc(self, writer, path, repo, repo_name):
"""Adding doc to writer this function itself fetches data from
the instance of vcs backend"""
node = self.get_node(repo, path)
#we just index the content of chosen files, and skip binary files
if node.extension in INDEX_EXTENSIONS and not node.is_binary:
u_content = node.content
if not isinstance(u_content, unicode):
log.warning(' >> %s Could not get this content as unicode '
'replacing with empty content', path)
u_content = u''
log.debug(' >> %s [WITH CONTENT]' % path)
log.debug(' >> %s' % path)
#just index file name without it's content
writer.add_document(owner=unicode(repo.contact),
repository=safe_unicode(repo.name),
repository=safe_unicode(repo_name),
path=safe_unicode(path),
content=u_content,
modtime=self.get_node_mtime(node),
extension=node.extension)
def build_index(self):
if os.path.exists(self.index_location):
log.debug('removing previous index')
rmtree(self.index_location)
if not os.path.exists(self.index_location):
os.mkdir(self.index_location)
idx = create_in(self.index_location, SCHEMA, indexname=IDX_NAME)
writer = idx.writer()
for repo in self.repo_paths.values():
log.debug('building index @ %s' % repo.path)
for idx_path in self.get_paths(repo):
self.add_doc(writer, idx_path, repo)
self.add_doc(writer, idx_path, repo, repo_name)
log.debug('>> COMMITING CHANGES <<')
writer.commit(merge=True)
log.debug('>>> FINISHED BUILDING INDEX <<<')
def update_index(self):
log.debug('STARTING INCREMENTAL INDEXING UPDATE')
idx = open_dir(self.index_location, indexname=self.indexname)
# The set of all paths in the index
indexed_paths = set()
# The set of all paths we need to re-index
to_index = set()
reader = idx.reader()
# Loop over the stored fields in the index
for fields in reader.all_stored_fields():
indexed_path = fields['path']
indexed_paths.add(indexed_path)
repo = self.repo_paths[fields['repository']]
node = self.get_node(repo, indexed_path)
# This file was deleted since it was indexed
log.debug('removing from index %s' % indexed_path)
writer.delete_by_term('path', indexed_path)
# Check if this file was changed since it was indexed
indexed_time = fields['modtime']
mtime = self.get_node_mtime(node)
if mtime > indexed_time:
# The file has changed, delete it and add it to the list of
# files to reindex
log.debug('adding to reindex list %s' % indexed_path)
to_index.add(indexed_path)
# Loop over the files in the filesystem
# Assume we have a function that gathers the filenames of the
# documents to be indexed
for path in self.get_paths(repo):
if path in to_index or path not in indexed_paths:
# This is either a file that's changed, or a new file
# that wasn't indexed before. So index it!
self.add_doc(writer, path, repo)
self.add_doc(writer, path, repo, repo_name)
log.debug('re indexing %s' % path)
log.debug('>>> FINISHED REBUILDING INDEX <<<')
def run(self, full_index=False):
"""Run daemon"""
if full_index or self.initial:
self.build_index()
self.update_index()
@@ -115,61 +115,61 @@
<thead>
<tr>
<th class="left">${_('Name')}</th>
<th class="left">${_('revision')}</th>
<th colspan="2" class="left">${_('action')}</th>
</thead>
<tbody>
%if c.user_repos:
%for repo in c.user_repos:
<td>
%if repo['dbrepo']['repo_type'] =='hg':
<img class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url("/images/icons/hgicon.png")}"/>
%elif repo['dbrepo']['repo_type'] =='git':
<img class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url("/images/icons/giticon.png")}"/>
%else:
%endif
%if repo['dbrepo']['private']:
<img class="icon" alt="${_('private')}" src="${h.url("/images/icons/lock.png")}"/>
<img class="icon" alt="${_('public')}" src="${h.url("/images/icons/lock_open.png")}"/>
${h.link_to(repo['repo'].name, h.url('summary_home',repo_name=repo['repo'].name),class_="repo_name")}
${h.link_to(repo['name'], h.url('summary_home',repo_name=repo['name']),class_="repo_name")}
%if repo['dbrepo_fork']:
<a href="${h.url('summary_home',repo_name=repo['dbrepo_fork']['repo_name'])}">
<img class="icon" alt="${_('public')}"
title="${_('Fork of')} ${repo['dbrepo_fork']['repo_name']}"
src="${h.url("/images/icons/arrow_divide.png")}"/></a>
</td>
<td><span class="tooltip" title="${repo['repo'].last_change}">${("r%s:%s") % (h.get_changeset_safe(repo['repo'],'tip').revision,h.short_id(h.get_changeset_safe(repo['repo'],'tip').raw_id))}</span></td>
<td><a href="${h.url('repo_settings_home',repo_name=repo['repo'].name)}" title="${_('edit')}"><img class="icon" alt="${_('private')}" src="${h.url("/images/icons/application_form_edit.png")}"/></a></td>
<td><a href="${h.url('repo_settings_home',repo_name=repo['name'])}" title="${_('edit')}"><img class="icon" alt="${_('private')}" src="${h.url("/images/icons/application_form_edit.png")}"/></a></td>
${h.form(url('repo_settings_delete', repo_name=repo['repo'].name),method='delete')}
${h.submit('remove_%s' % repo['repo'].name,'',class_="delete_icon action_button",onclick="return confirm('Confirm to delete this repository');")}
${h.form(url('repo_settings_delete', repo_name=repo['name']),method='delete')}
${h.submit('remove_%s' % repo['name'],'',class_="delete_icon action_button",onclick="return confirm('Confirm to delete this repository');")}
${h.end_form()}
</tr>
%endfor
${_('No repositories yet')}
%if h.HasPermissionAny('hg.admin','hg.create.repository')():
${h.link_to(_('create one now'),h.url('admin_settings_create_repository'))}
</tbody>
</table>
</div>
<script type="text/javascript">
var D = YAHOO.util.Dom;
var E = YAHOO.util.Event;
var S = YAHOO.util.Selector;
var q_filter = D.get('q_filter');
var F = YAHOO.namespace('q_filter');
E.on(q_filter,'click',function(){
@@ -46,46 +46,46 @@
h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path))}
</dd>
<dt>${_('History')}</dt>
<dd>
<div>
${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')}
${h.hidden('diff2',c.file.last_changeset.raw_id)}
${h.select('diff1',c.file.last_changeset.raw_id,c.file_history)}
${h.submit('diff','diff to revision',class_="ui-button")}
${h.submit('show_rev','show at revision',class_="ui-button")}
</dl>
<div id="body" class="codeblock">
<div class="code-header">
<div class="revision">${c.file.name}@r${c.file.last_changeset.revision}:${h.short_id(c.file.last_changeset.raw_id)}</div>
<div class="commit">"${c.file.message}"</div>
<div class="code-body">
%if c.file.is_binary:
${_('Binary file')}
% if c.file.size < c.cut_off_limit:
${h.pygmentize_annotation(c.file,linenos=True,anchorlinenos=True,lineanchors='S',cssclass="code-highlight")}
${h.pygmentize_annotation(c.repo_name,c.file,linenos=True,anchorlinenos=True,lineanchors='S',cssclass="code-highlight")}
${_('File is to big to display')} ${h.link_to(_('show as raw'),
h.url('files_raw_home',repo_name=c.repo_name,revision=c.cs.revision,f_path=c.f_path))}
YAHOO.util.Event.onDOMReady(function(){
YAHOO.util.Event.addListener('show_rev','click',function(e){
YAHOO.util.Event.preventDefault(e);
var cs = YAHOO.util.Dom.get('diff1').value;
var url = "${h.url('files_annotate_home',repo_name=c.repo_name,revision='__CS__',f_path=c.f_path)}".replace('__CS__',cs);
window.location = url;
});
</script>
</%def>
\ No newline at end of file
## -*- coding: utf-8 -*-
%for repo in c.repos_list:
<li>
<img src="${h.url("/images/icons/lock.png")}" alt="${_('Private repository')}" class="repo_switcher_type"/>
${h.link_to(repo['name'].name,h.url('summary_home',repo_name=repo['name']),class_="%s" % repo['dbrepo']['repo_type'])}
${h.link_to(repo['name'],h.url('summary_home',repo_name=repo['name']),class_="%s" % repo['dbrepo']['repo_type'])}
</li>
<img src="${h.url("/images/icons/lock_open.png")}" alt="${_('Public repository')}" class="repo_switcher_type" />
<%inherit file="/base/base.html"/>
<%def name="title()">
${c.repo_name} ${_('Summary')} - ${c.rhodecode_name}
<%def name="breadcrumbs_links()">
${h.link_to(u'Home',h.url('/'))}
»
${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
${h.link_to(c.dbrepo.just_name,h.url('summary_home',repo_name=c.repo_name))}
${_('summary')}
<%def name="page_nav()">
${self.menu('summary')}
<%def name="main()">
<div class="box box-left">
<!-- box / title -->
<div class="title">
${self.breadcrumbs()}
<!-- end box / title -->
<div class="form">
<div class="fields">
<div class="field">
<div class="label">
<label>${_('Name')}:</label>
<div class="input-short">
%if c.rhodecode_user.username != 'default':
%if c.following:
<span id="follow_toggle" class="following" title="${_('Stop following this repository')}"
onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
</span>
<span id="follow_toggle" class="follow" title="${_('Start following this repository')}"
%endif:
%if c.dbrepo.repo_type =='hg':
<img style="margin-bottom:2px" class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url("/images/icons/hgicon.png")}"/>
%if c.dbrepo.repo_type =='git':
<img style="margin-bottom:2px" class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url("/images/icons/giticon.png")}"/>
%if c.dbrepo.private:
<img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="${h.url("/images/icons/lock.png")}"/>
<img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="${h.url("/images/icons/lock_open.png")}"/>
<span style="font-size: 1.6em;font-weight: bold;vertical-align: baseline;">${c.repo.name}</span>
<br/>
<span style="font-size: 1.6em;font-weight: bold;vertical-align: baseline;">${h.repo_link(c.dbrepo.groups_and_repo)}</span>
%if c.dbrepo.fork:
<span style="margin-top:5px">
<a href="${h.url('summary_home',repo_name=c.dbrepo.fork.repo_name)}">
title="${_('Fork of')} ${c.dbrepo.fork.repo_name}"
src="${h.url("/images/icons/arrow_divide.png")}"/>
${_('Fork of')} ${c.dbrepo.fork.repo_name}
</a>
%if c.dbrepo.clone_uri:
<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}
@@ -88,110 +88,113 @@
${c.dbrepo.description}
<label>${_('Contact')}:</label>
<div class="gravatar">
<img alt="gravatar" src="${h.gravatar_url(c.dbrepo.user.email)}"/>
${_('Username')}: ${c.dbrepo.user.username}<br/>
${_('Name')}: ${c.dbrepo.user.name} ${c.dbrepo.user.lastname}<br/>
${_('Email')}: <a href="mailto:${c.dbrepo.user.email}">${c.dbrepo.user.email}</a>
<label>${_('Last change')}:</label>
${h.age(c.repo.last_change)} - ${c.repo.last_change}
${_('by')} ${h.get_changeset_safe(c.repo,'tip').author}
<b>${'r%s:%s' % (h.get_changeset_safe(c.rhodecode_repo,'tip').revision,
h.get_changeset_safe(c.rhodecode_repo,'tip').short_id)}</b> -
<span class="tooltip" title="${c.rhodecode_repo.last_change}">
${h.age(c.rhodecode_repo.last_change)}</span><br/>
${_('by')} ${h.get_changeset_safe(c.rhodecode_repo,'tip').author}
<label>${_('Clone url')}:</label>
<input type="text" id="clone_url" readonly="readonly" value="hg clone ${c.clone_repo_url}" size="70"/>
<label>${_('Trending source files')}:</label>
<div id="lang_stats"></div>
<label>${_('Download')}:</label>
%if len(c.repo.revisions) == 0:
%if len(c.rhodecode_repo.revisions) == 0:
${_('There are no downloads yet')}
%elif c.enable_downloads is False:
${_('Downloads are disabled for this repository')}
%if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
[${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name))}]
${h.select('download_options',c.repo.get_changeset().raw_id,c.download_options)}
%for cnt,archive in enumerate(c.repo._get_archives()):
${h.select('download_options',c.rhodecode_repo.get_changeset().raw_id,c.download_options)}
%for cnt,archive in enumerate(c.rhodecode_repo._get_archives()):
%if cnt >=1:
|
<span class="tooltip" title="${_('Download %s as %s') %('tip',archive['type'])}"
id="${archive['type']+'_link'}">${h.link_to(archive['type'],
h.url('files_archive_home',repo_name=c.repo.name,
h.url('files_archive_home',repo_name=c.dbrepo.repo_name,
fname='tip'+archive['extension']),class_="archive_icon")}</span>
<label>${_('Feeds')}:</label>
${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.repo.name,api_key=c.rhodecode_user.api_key),class_='rss_icon')}
${h.link_to(_('Atom'),h.url('atom_feed_home',repo_name=c.repo.name,api_key=c.rhodecode_user.api_key),class_='atom_icon')}
${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key),class_='rss_icon')}
${h.link_to(_('Atom'),h.url('atom_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key),class_='atom_icon')}
${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.repo.name),class_='rss_icon')}
${h.link_to(_('Atom'),h.url('atom_feed_home',repo_name=c.repo.name),class_='atom_icon')}
${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.dbrepo.repo_name),class_='rss_icon')}
${h.link_to(_('Atom'),h.url('atom_feed_home',repo_name=c.dbrepo.repo_name),class_='atom_icon')}
YUE.onDOMReady(function(e){
id = 'clone_url';
YUE.on(id,'click',function(e){
YUD.get('clone_url').select();
})
var data = ${c.trending_languages|n};
var total = 0;
var no_data = true;
for (k in data){
total += data[k];
no_data = false;
}
var tbl = document.createElement('table');
tbl.setAttribute('class','trending_language_tbl');
var cnt =0;
cnt+=1;
@@ -244,51 +247,51 @@
if(no_data){
var tr = document.createElement('tr');
var td1 = document.createElement('td');
td1.innerHTML = "${c.no_data_msg}";
tr.appendChild(td1);
tbl.appendChild(tr);
YUD.get('lang_stats').appendChild(tbl);
YUE.on('code_stats_show_more','click',function(){
l = YUD.getElementsByClassName('stats_hidden')
for (e in l){
YUD.setStyle(l[e],'display','');
};
YUD.setStyle(YUD.get('code_stats_show_more'),
'display','none');
YUE.on('download_options','change',function(e){
var new_cs = e.target.options[e.target.selectedIndex];
var tmpl_links = {}
tmpl_links['${archive['type']}'] = '${h.link_to(archive['type'],
fname='__CS__'+archive['extension']),class_="archive_icon")}';
for(k in tmpl_links){
var s = YUD.get(k+'_link')
title_tmpl = "${_('Download %s as %s') % ('__CS_NAME__',archive['type'])}";
s.title = title_tmpl.replace('__CS_NAME__',new_cs.text)
s.innerHTML = tmpl_links[k].replace('__CS__',new_cs.value);
<div class="box box-right" style="min-height:455px">
<h5>${_('Commit activity by day / author')}</h5>
<div class="table">
%if c.no_data:
from rhodecode.tests import *
class TestReposGroupsController(TestController):
def test_index(self):
response = self.app.get(url('repos_groups'))
# Test response...
def test_index_as_xml(self):
response = self.app.get(url('formatted_repos_groups', format='xml'))
def test_create(self):
response = self.app.post(url('repos_groups'))
def test_new(self):
response = self.app.get(url('new_repos_group'))
def test_new_as_xml(self):
response = self.app.get(url('formatted_new_repos_group', format='xml'))
def test_update(self):
response = self.app.put(url('repos_group', id=1))
def test_update_browser_fakeout(self):
response = self.app.post(url('repos_group', id=1), params=dict(_method='put'))
def test_delete(self):
response = self.app.delete(url('repos_group', id=1))
def test_delete_browser_fakeout(self):
response = self.app.post(url('repos_group', id=1), params=dict(_method='delete'))
def test_show(self):
response = self.app.get(url('repos_group', id=1))
def test_show_as_xml(self):
response = self.app.get(url('formatted_repos_group', id=1, format='xml'))
def test_edit(self):
response = self.app.get(url('edit_repos_group', id=1))
def test_edit_as_xml(self):
response = self.app.get(url('formatted_edit_repos_group', id=1, format='xml'))
Status change: