@@ -15,40 +15,65 @@ delete a repository You can easy restore
from the repository name, and internal repository storage (.hg/.git)
Follow current branch in file view
----------------------------------
In file view when this checkbox is checked the << and >> arrows will jump
to changesets within the same branch currently viewing. So for example
if someone is viewing files at 'beta' branch and marks `follow current branch`
checkbox the << and >> buttons will only show him revisions for 'beta' branch
Compare view from changelog
---------------------------
Checkboxes in compare view allow users to view combined compare view. You can
only show the range between the first and last checkbox (no cherry pick).
Clicking more than one checkbox will activate a link in top saying
`Show selected changes <from-rev> -> <to-rev>` clicking this will bring
compare view
Compare view is also available from the journal on pushes having more than
one changeset
Non changeable repository urls
------------------------------
Due to complicated nature of repository grouping, often urls of repositories
can change.
example::
#before
http://server.com/repo_name
# after insertion to test_group group the url will be
http://server.com/test_group/repo_name
This can be an issue for build systems and any other hardcoded scripts, moving
repository to a group leads to a need for changing external systems. To
overcome this RhodeCode introduces a non changable replacement url. It's
simply an repository ID prefixed with `_` above urls are also accessible as::
http://server.com/_<ID>
Since ID are always the same moving the repository will not affect such url.
the _<ID> syntax can be used anywhere in the system so urls with repo_name
for changelogs, files and other can be exchanged with _<ID> syntax.
Mailing
-------
When administrator will fill up the mailing settings in .ini files
RhodeCode will send mails on user registration, or when RhodeCode errors occur
on errors the mails will have a detailed traceback of error.
Trending source files
---------------------
Trending source files are calculated based on pre defined dict of known
types and extensions. If You miss some extension or Would like to scan some
custom files it's possible to add new types in `LANGUAGES_EXTENSIONS_MAP` dict
located in `/rhodecode/lib/celerylib/tasks.py`
\ No newline at end of file
"""
Routes configuration
The more specific and detailed routes should be defined first so they
may take precedent over the more generic routes. For more information
refer to the routes manual at http://routes.groovie.org/docs/
from __future__ import with_statement
from routes import Mapper
# prefix for non repository related links needs to be prefixed with `/`
ADMIN_PREFIX = '/_admin'
def make_map(config):
"""Create, configure and return the routes Mapper"""
rmap = Mapper(directory=config['pylons.paths']['controllers'],
always_scan=config['debug'])
rmap.minimization = False
rmap.explicit = False
from rhodecode.lib.utils import is_valid_repo
from rhodecode.lib.utils import is_valid_repos_group
def check_repo(environ, match_dict):
check for valid repository for proper 404 handling
:param environ:
:param match_dict:
from rhodecode.model.db import Repository
repo_name = match_dict.get('repo_name')
try:
by_id = repo_name.split('_')
if len(by_id) == 2 and by_id[1].isdigit():
repo_name = Repository.get(by_id[1]).repo_name
match_dict['repo_name'] = repo_name
except:
pass
return is_valid_repo(repo_name, config['base_path'])
def check_group(environ, match_dict):
check for valid repositories group for proper 404 handling
repos_group_name = match_dict.get('group_name')
return is_valid_repos_group(repos_group_name, config['base_path'])
def check_int(environ, match_dict):
return match_dict.get('id').isdigit()
# The ErrorController route (handles 404/500 error pages); it should
# likely stay at the top, ensuring it can always be resolved
rmap.connect('/error/{action}', controller='error')
rmap.connect('/error/{action}/{id}', controller='error')
#==========================================================================
# CUSTOM ROUTES HERE
@@ -71,55 +71,62 @@ class SummaryController(BaseRepoControll
c.following = self.scm_model.is_following_repo(repo_name,
self.rhodecode_user.user_id)
def url_generator(**kw):
return url('shortlog_home', repo_name=repo_name, size=10, **kw)
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 = '@'
parsed_url = urlparse(url.current(qualified=True))
default_clone_uri = '{scheme}://{user}{pass}{netloc}{path}'
uri_tmpl = config.get('clone_uri', default_clone_uri)
uri_tmpl = uri_tmpl.replace('{', '%(').replace('}', ')s')
uri = uri_tmpl % {'user': username,
'pass': password,
'scheme': parsed_url.scheme,
'netloc': parsed_url.netloc,
'path':parsed_url.path}
uri_dict = {
'user': username,
'path': parsed_url.path
}
uri = uri_tmpl % uri_dict
# generate another clone url by id
uri_dict.update({'path': '/_%s' % c.dbrepo.repo_id})
uri_id = uri_tmpl % uri_dict
c.clone_repo_url = uri
c.clone_repo_url_id = uri_id
c.repo_tags = OrderedDict()
for name, hash in c.rhodecode_repo.tags.items()[:10]:
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.rhodecode_repo.branches.items()[:10]:
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.show_stats = True
"""The base Controller API
Provides the BaseController class for subclassing.
import logging
import time
import traceback
from paste.auth.basic import AuthBasicAuthenticator
from pylons import config, tmpl_context as c, request, session, url
from pylons.controllers import WSGIController
from pylons.controllers.util import redirect
from pylons.templating import render_mako as render
from rhodecode import __version__, BACKENDS
from rhodecode.lib import str2bool
from rhodecode.lib.auth import AuthUser, get_container_username, authfunc,\
HasPermissionAnyMiddleware
from rhodecode.lib.utils import get_repo_slug, invalidate_cache
from rhodecode.model import meta
from rhodecode.model.notification import NotificationModel
from rhodecode.model.scm import ScmModel
log = logging.getLogger(__name__)
class BaseVCSController(object):
def __init__(self, application, config):
self.application = application
self.config = config
# base path of repo locations
self.basepath = self.config['base_path']
#authenticate this mercurial request using authfunc
self.authenticate = AuthBasicAuthenticator('', authfunc)
self.ipaddr = '0.0.0.0'
def _get_by_id(self, repo_name):
Get's a special pattern _<ID> from clone url and tries to replace it
with a repository_name for support of _<ID> non changable urls
:param repo_name:
data = repo_name.split('/')
if len(data) >= 2:
by_id = data[1].split('_')
_repo_name = Repository.get(by_id[1]).repo_name
data[1] = _repo_name
log.debug('Failed to extract repo_name from id %s' % (
traceback.format_exc()
)
return '/'.join(data)
def _invalidate_cache(self, repo_name):
Set's cache for this repository for invalidation on next access
:param repo_name: full repo name, also a cache key
invalidate_cache('get_repo_cached_%s' % repo_name)
def _check_permission(self, action, user, repo_name):
Checks permissions using action (push/pull) user and repository
name
:param action: push or pull action
:param user: user instance
:param repo_name: repository name
if action == 'push':
if not HasPermissionAnyMiddleware('repository.write',
'repository.admin')(user,
repo_name):
return False
#any other action need at least read permission
if not HasPermissionAnyMiddleware('repository.read',
'repository.write',
return True
def __call__(self, environ, start_response):
start = time.time()
return self._handle_request(environ, start_response)
finally:
log = logging.getLogger('rhodecode.' + self.__class__.__name__)
log.debug('Request time: %.3fs' % (time.time() - start))
meta.Session.remove()
class BaseController(WSGIController):
def __before__(self):
c.rhodecode_version = __version__
c.rhodecode_name = config.get('rhodecode_title')
c.use_gravatar = str2bool(config.get('use_gravatar'))
c.ga_code = config.get('rhodecode_ga_code')
c.repo_name = get_repo_slug(request)
c.backends = BACKENDS.keys()
c.unread_notifications = NotificationModel()\
.get_unread_cnt_for_user(c.rhodecode_user.user_id)
self.cut_off_limit = int(config.get('cut_off_limit'))
self.sa = meta.Session
@@ -196,48 +196,49 @@ class SimpleGit(BaseVCSController):
log.error(traceback.format_exc())
return HTTPInternalServerError()(environ, start_response)
def __make_app(self, repo_name, repo_path):
Make an wsgi application using dulserver
:param repo_name: name of the repository
:param repo_path: full path to the repository
_d = {'/' + repo_name: Repo(repo_path)}
backend = dulserver.DictBackend(_d)
gitserve = HTTPGitApplication(backend)
return gitserve
def __get_repository(self, environ):
Get's repository name out of PATH_INFO header
:param environ: environ where PATH_INFO is stored
environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO'])
repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
if repo_name.endswith('/'):
repo_name = repo_name.rstrip('/')
raise
repo_name = repo_name.split('/')[0]
return repo_name
def __get_user(self, username):
return User.get_by_username(username)
def __get_action(self, environ):
"""Maps git request commands into a pull or push command.
service = environ['QUERY_STRING'].split('=')
if len(service) > 1:
service_cmd = service[1]
mapping = {'git-receive-pack': 'push',
'git-upload-pack': 'pull',
@@ -159,56 +159,56 @@ class SimpleHg(BaseVCSController):
if is_valid_repo(repo_name, self.basepath) is False:
return HTTPNotFound()(environ, start_response)
# invalidate cache on push
self._invalidate_cache(repo_name)
app = self.__make_app(repo_path, baseui, extras)
return app(environ, start_response)
except RepoError, e:
if str(e).find('not found') != -1:
except Exception:
def __make_app(self, repo_name, baseui, extras):
Make an wsgi application using hgweb, and inject generated baseui
instance, additionally inject some extras into ui object
return hgweb_mod.hgweb(repo_name, name=repo_name, baseui=baseui)
Maps mercurial request commands into a clone,pull or push command.
This should always return a valid command string
mapping = {'changegroup': 'pull',
'changegroupsubset': 'pull',
'stream_out': 'pull',
'listkeys': 'pull',
'unbundle': 'push',
@@ -1329,51 +1329,53 @@ tbody .yui-dt-editable { cursor: pointer
#content div.box div.form div.fields div.field div.input.summary-short {
margin: 0 0 0 110px;
#content div.box div.form div.fields div.field div.file {
margin: 0 0 0 200px;
#content div.box-left div.form div.fields div.field div.input,#content div.box-right div.form div.fields div.field div.input
{
margin: 0 0 0 0px;
#content div.box div.form div.fields div.field div.input input {
background: #FFF;
border-top: 1px solid #b3b3b3;
border-left: 1px solid #b3b3b3;
border-right: 1px solid #eaeaea;
border-bottom: 1px solid #eaeaea;
color: #000;
font-size: 11px;
margin: 0;
padding: 7px 7px 6px;
#content div.box div.form div.fields div.field div.input input#clone_url{
#content div.box div.form div.fields div.field div.input input#clone_url,
#content div.box div.form div.fields div.field div.input input#clone_url_id
font-size: 16px;
padding: 2px 7px 2px;
padding: 2px;
#content div.box div.form div.fields div.field div.file input {
background: none repeat scroll 0 0 #FFFFFF;
border-color: #B3B3B3 #EAEAEA #EAEAEA #B3B3B3;
border-style: solid;
border-width: 1px;
color: #000000;
#content div.box div.form div.fields div.field div.input input.small {
width: 30%;
#content div.box div.form div.fields div.field div.input input.medium {
width: 55%;
#content div.box div.form div.fields div.field div.input input.large {
width: 85%;
@@ -3013,49 +3015,60 @@ div.gravatar img {
background-image: -webkit-linear-gradient(top, #F4F4F4, #DADADA) );
background-image: -o-linear-gradient(top, #F4F4F4, #DADADA) );
background-image: linear-gradient(top, #F4F4F4, #DADADA);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#F4F4F4', endColorstr='#DADADA', GradientType=0);
border-top: 1px solid #DDD;
border-left: 1px solid #c6c6c6;
border-right: 1px solid #DDD;
border-bottom: 1px solid #c6c6c6;
color: #515151;
outline: none;
margin: 0px 3px 3px 0px;
-webkit-border-radius: 4px 4px 4px 4px !important;
-khtml-border-radius: 4px 4px 4px 4px !important;
-moz-border-radius: 4px 4px 4px 4px !important;
border-radius: 4px 4px 4px 4px !important;
cursor: pointer !important;
padding: 3px 3px 3px 3px;
background-position: 0 -15px;
.ui-btn.xsmall{
padding: 1px 2px 1px 1px;
.ui-btn.clone{
padding: 5px 2px 6px 1px;
margin: 0px -4px 3px 0px;
-webkit-border-radius: 4px 0px 0px 4px !important;
-khtml-border-radius: 4px 0px 0px 4px !important;
-moz-border-radius: 4px 0px 0px 4px !important;
border-radius: 4px 0px 0px 4px !important;
width: 100px;
text-align: center;
float: left;
position: absolute;
.ui-btn:focus {
.ui-btn:hover{
background-position: 0 0px;
text-decoration: none;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25), 0 0 3px #FFFFFF !important;
.ui-btn.red{
color:#fff;
background-color: #c43c35;
background-repeat: repeat-x;
background-image: -khtml-gradient(linear, left top, left bottom, from(#ee5f5b), to(#c43c35));
background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35);
background-image: -ms-linear-gradient(top, #ee5f5b, #c43c35);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ee5f5b), color-stop(100%, #c43c35));
background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35);
background-image: -o-linear-gradient(top, #ee5f5b, #c43c35);
background-image: linear-gradient(top, #ee5f5b, #c43c35);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0);
border-color: #c43c35 #c43c35 #882a25;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
@@ -3079,49 +3092,50 @@ div.gravatar img {
.ui-btn.green{
background-color: #57a957;
background-image: -khtml-gradient(linear, left top, left bottom, from(#62c462), to(#57a957));
background-image: -moz-linear-gradient(top, #62c462, #57a957);
background-image: -ms-linear-gradient(top, #62c462, #57a957);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #62c462), color-stop(100%, #57a957));
background-image: -webkit-linear-gradient(top, #62c462, #57a957);
background-image: -o-linear-gradient(top, #62c462, #57a957);
background-image: linear-gradient(top, #62c462, #57a957);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0);
border-color: #57a957 #57a957 #3d773d;
ins,div.options a:hover {
img,
#header #header-inner #quick li a:hover span.normal,
#header #header-inner #quick li ul li.last,
#content div.box div.form div.fields div.field div.textarea table td table td a,
#clone_url
#clone_url,
#clone_url_id
border: none;
img.icon,.right .merge img {
vertical-align: bottom;
#header ul#logged-user,#content div.box div.title ul.links,
#content div.box div.message div.dismiss,
#content div.box div.traffic div.legend ul
float: right;
padding: 0;
#header #header-inner #home,#header #header-inner #logo,
#content div.box ul.left,#content div.box ol.left,
#content div.box div.pagination-left,div#commit_history,
div#legend_data,div#legend_container,div#legend_choices
@@ -53,96 +53,99 @@
onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
</span>
%else:
<span id="follow_toggle" class="follow" title="${_('Start following this repository')}"
%endif
%endif:
##REPO TYPE
%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')}"/>
##PUBLIC/PRIVATE
%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')}"/>
##REPO NAME
<span class="repo_name">${h.repo_link(c.dbrepo.groups_and_repo)}</span>
<span class="repo_name" title="${_('Non changable ID %s') % c.dbrepo.repo_id}">${h.repo_link(c.dbrepo.groups_and_repo)}</span>
##FORK
%if c.dbrepo.fork:
<div style="margin-top:5px;clear:both"">
<a href="${h.url('summary_home',repo_name=c.dbrepo.fork.repo_name)}"><img class="icon" alt="${_('public')}" title="${_('Fork of')} ${c.dbrepo.fork.repo_name}" src="${h.url('/images/icons/arrow_divide.png')}"/>
${_('Fork of')} ${c.dbrepo.fork.repo_name}
</a>
</div>
##REMOTE
%if c.dbrepo.clone_uri:
<div style="margin-top:5px;clear:both">
<a href="${h.url(str(h.hide_credentials(c.dbrepo.clone_uri)))}"><img class="icon" alt="${_('remote clone')}" title="${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}" src="${h.url('/images/icons/connect.png')}"/>
${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}
<div class="field">
<div class="label-summary">
<label>${_('Description')}:</label>
<div class="input ${summary(c.show_stats)} desc">${h.urlify_text(c.dbrepo.description)}</div>
<label>${_('Contact')}:</label>
<div class="input ${summary(c.show_stats)}">
<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>${_('Clone url')}:</label>
<input type="text" id="clone_url" readonly="readonly" value="${c.clone_repo_url}" size="70"/>
<div style="display:none" id="clone_by_name" class="ui-btn clone">${_('Show by Name')}</div>
<div id="clone_by_id" class="ui-btn clone">${_('Show by ID')}</div>
<input style="width:80%;margin-left:105px" type="text" id="clone_url" readonly="readonly" value="${c.clone_repo_url}"/>
<input style="display:none;width:80%;margin-left:105px" type="text" id="clone_url_id" readonly="readonly" value="${c.clone_repo_url_id}"/>
<label>${_('Trending files')}:</label>
%if c.show_stats:
<div id="lang_stats"></div>
${_('Statistics 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),class_="ui-btn")}
<label>${_('Download')}:</label>
@@ -219,48 +222,70 @@
<div class="box" style="background-color: #FAFAFA">
<div class="title">
<div class="breadcrumbs"><a href="${h.url('files_home',repo_name=c.repo_name,revision='tip',f_path=c.readme_file)}">${c.readme_file}</a></div>
<div class="readme">
<div class="readme_box">
${c.readme_data|n}
<script type="text/javascript">
var clone_url = 'clone_url';
YUE.on(clone_url,'click',function(e){
if(YUD.hasClass(clone_url,'selected')){
return
else{
YUD.addClass(clone_url,'selected');
YUD.get(clone_url).select();
})
YUE.on('clone_by_name','click',function(e){
// show url by name and hide name button
YUD.setStyle('clone_url','display','');
YUD.setStyle('clone_by_name','display','none');
// hide url by id and show name button
YUD.setStyle('clone_by_id','display','');
YUD.setStyle('clone_url_id','display','none');
YUE.on('clone_by_id','click',function(e){
// show url by id and hide id button
YUD.setStyle('clone_by_id','display','none');
YUD.setStyle('clone_url_id','display','');
// hide url by name and show id button
YUD.setStyle('clone_by_name','display','');
YUD.setStyle('clone_url','display','none');
var tmpl_links = {};
%for cnt,archive in enumerate(c.rhodecode_repo._get_archives()):
tmpl_links["${archive['type']}"] = '${h.link_to('__NAME__', h.url('files_archive_home',repo_name=c.dbrepo.repo_name, fname='__CS__'+archive['extension'],subrepos='__SUB__'),class_='archive_icon ui-btn')}';
%endfor
YUE.on(['download_options','archive_subrepos'],'change',function(e){
var sm = YUD.get('download_options');
var new_cs = sm.options[sm.selectedIndex];
for(k in tmpl_links){
var s = YUD.get(k+'_link');
if(s){
var title_tmpl = "${_('Download %s as %s') % ('__CS_NAME__','__CS_EXT__')}";
title_tmpl= title_tmpl.replace('__CS_NAME__',new_cs.text);
title_tmpl = title_tmpl.replace('__CS_EXT__',k);
var url = tmpl_links[k].replace('__CS__',new_cs.value);
var subrepos = YUD.get('archive_subrepos').checked;
url = url.replace('__SUB__',subrepos);
url = url.replace('__NAME__',title_tmpl);
s.innerHTML = url
});
@@ -147,60 +147,64 @@ def set_anonymous_access(enable=True):
sa.commit()
def get_anonymous_access():
sa = get_session()
return sa.query(User).filter(User.username == 'default').one().active
#==============================================================================
# TESTS
def test_clone_with_credentials(no_errors=False, repo=HG_REPO, method=METHOD,
seq=None):
cwd = path = jn(TESTS_TMP_PATH, repo)
if seq == None:
seq = _RandomNameSequence().next()
shutil.rmtree(path, ignore_errors=True)
os.makedirs(path)
#print 'made dirs %s' % jn(path)
except OSError:
clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s' % \
{'user':USER,
'pass':PASS,
'host':HOST,
'cloned_repo':repo, }
dest = path + seq
if method == 'pull':
stdout, stderr = Command(cwd).execute('hg', method, '--cwd', dest, clone_url)
stdout, stderr = Command(cwd).execute('hg', method, clone_url, dest)
if no_errors is False:
assert """adding file changes""" in stdout, 'no messages about cloning'
assert """abort""" not in stderr , 'got error from clone'
if __name__ == '__main__':
create_test_user(force=False)
seq = None
METHOD = sys.argv[3]
if METHOD == 'pull':
test_clone_with_credentials(repo=sys.argv[1], method='clone',
seq=seq)
s = time.time()
for i in range(int(sys.argv[2])):
for i in range(1, int(sys.argv[2]) + 1):
print 'take', i
test_clone_with_credentials(repo=sys.argv[1], method=METHOD,
print 'time taken %.3f' % (time.time() - s)
except Exception, e:
sys.exit('stop on %s' % e)
from rhodecode.tests import *
from rhodecode.lib.utils import invalidate_cache
class TestSummaryController(TestController):
def test_index(self):
self.log_user()
ID = Repository.get_by_repo_name(HG_REPO).repo_id
response = self.app.get(url(controller='summary',
action='index', repo_name=HG_REPO))
action='index',
repo_name=HG_REPO))
#repo type
self.assertTrue("""<img style="margin-bottom:2px" class="icon" """
response.mustcontain("""<img style="margin-bottom:2px" class="icon" """
"""title="Mercurial repository" alt="Mercurial """
"""repository" src="/images/icons/hgicon.png"/>"""
in response.body)
"""repository" src="/images/icons/hgicon.png"/>""")
"""title="public repository" alt="public """
"""repository" src="/images/icons/lock_open.png"/>"""
"""repository" src="/images/icons/lock_open.png"/>""")
#codes stats
self._enable_stats()
invalidate_cache('get_repo_cached_%s' % HG_REPO)
response = self.app.get(url(controller='summary', action='index',
self.assertTrue("""var data = {"py": {"count": 42, "desc": """
"""["Python"]}, "rst": {"count": 11, "desc": """
"""["Rst"]}, "sh": {"count": 2, "desc": ["Bash"]}, """
""""makefile": {"count": 1, "desc": ["Makefile", """
""""Makefile"]}, "cfg": {"count": 1, "desc": ["Ini"]},"""
""" "css": {"count": 1, "desc": ["Css"]}, "bat": """
"""{"count": 1, "desc": ["Batch"]}};"""
# clone url...
self.assertTrue("""<input type="text" id="clone_url" readonly="readonly" value="http://test_admin@localhost:80/%s" size="70"/>""" % HG_REPO in response.body)
response.mustcontain("""<input style="width:80%;margin-left:105px" type="text" id="clone_url" readonly="readonly" value="http://test_admin@localhost:80/vcs_test_hg"/>""")
response.mustcontain("""<input style="display:none;width:80%;margin-left:105px" type="text" id="clone_url_id" readonly="readonly" value="http://test_admin@localhost:80/_1"/>""")
def test_index_by_id(self):
repo_name='_%s' % ID))
def _enable_stats(self):
r = Repository.get_by_repo_name(HG_REPO)
r.enable_statistics = True
self.Session.add(r)
self.Session.commit()
Status change: