@@ -14,49 +14,54 @@
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# 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, see <http://www.gnu.org/licenses/>.
import traceback
import calendar
import logging
from time import mktime
from datetime import datetime, timedelta, date
from datetime import timedelta, date
from itertools import product
from vcs.exceptions import ChangesetError
from vcs.exceptions import ChangesetError, EmptyRepositoryError, \
NodeDoesNotExistError
from pylons import tmpl_context as c, request, url
from pylons.i18n.translation import _
from rhodecode.model.db import Statistics, Repository
from rhodecode.model.repo import RepoModel
from rhodecode.model.db import Statistics
from rhodecode.lib import ALL_READMES, ALL_EXTS
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
from rhodecode.lib.base import BaseRepoController, render
from rhodecode.lib.utils import EmptyChangeset
from rhodecode.lib.markup_renderer import MarkupRenderer
from rhodecode.lib.celerylib import run_task
from rhodecode.lib.celerylib.tasks import get_commits_stats, \
LANGUAGES_EXTENSIONS_MAP
from rhodecode.lib.helpers import RepoPage
from rhodecode.lib.compat import json, OrderedDict
log = logging.getLogger(__name__)
README_FILES = [''.join([x[0][0], x[1][0]]) for x in
sorted(list(product(ALL_READMES, ALL_EXTS)),
key=lambda y:y[0][1] + y[1][1])]
class SummaryController(BaseRepoController):
@LoginRequired()
@HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
'repository.admin')
def __before__(self):
super(SummaryController, self).__before__()
def index(self, repo_name):
e = request.environ
@@ -152,32 +157,58 @@ class SummaryController(BaseRepoControll
c.stats_percentage = '%.2f' % ((float((last_rev)) /
c.repo_last_rev) * 100)
else:
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.rhodecode_repo)
c.readme_data,c.readme_file = self.__get_readme_data()
return render('summary/summary.html')
def __get_readme_data(self):
readme_data = None
readme_file = None
try:
cs = c.rhodecode_repo.get_changeset('tip')
renderer = MarkupRenderer()
for f in README_FILES:
readme = cs.get_node(f)
readme_file = f
readme_data = renderer.render(readme.content, f)
break
except NodeDoesNotExistError:
continue
except ChangesetError:
pass
except EmptyRepositoryError:
except Exception:
log.error(traceback.format_exc())
return readme_data, readme_file
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
@@ -57,24 +57,52 @@ def __get_lem():
return dict(d)
# language map is also used by whoosh indexer, which for those specified
# extensions will index it's content
LANGUAGES_EXTENSIONS_MAP = __get_lem()
# Additional mappings that are not present in the pygments lexers
# NOTE: that this will overide any mappings in LANGUAGES_EXTENSIONS_MAP
ADDITIONAL_MAPPINGS = {'xaml': 'XAML'}
LANGUAGES_EXTENSIONS_MAP.update(ADDITIONAL_MAPPINGS)
# list of readme files to search in file tree and display in summary
# attached weights defines the search order lower is first
ALL_READMES = [
('readme', 0), ('README', 0), ('Readme', 0),
('doc/readme', 1), ('doc/README', 1), ('doc/Readme', 1),
('Docs/readme', 2), ('Docs/README', 2), ('Docs/Readme', 2),
('DOCS/readme', 2), ('DOCS/README', 2), ('DOCS/Readme', 2),
('docs/readme', 2), ('docs/README', 2), ('docs/Readme', 2),
]
# extension together with weights to search lower is first
RST_EXTS = [
('', 0), ('.rst', 1),('.rest', 1),
('.RST', 2) ,('.REST', 2),
('.txt', 3), ('.TXT', 3)
MARKDOWN_EXTS = [
('.md', 1), ('.MD', 1),
('.mkdn', 2), ('.MKDN', 2),
('.mdown', 3), ('.MDOWN', 3),
('.markdown', 4), ('.MARKDOWN', 4)
PLAIN_EXTS = [('.text', 2),('.TEXT', 2)]
ALL_EXTS = MARKDOWN_EXTS + RST_EXTS + PLAIN_EXTS
def str2bool(_str):
"""
returs True/False value from given string, it tries to translate the
string into boolean
:param _str: string value to translate into boolean
:rtype: boolean
:returns: boolean from given string
if _str is None:
return False
@@ -3002,13 +3002,105 @@ div#legend_container table td,div#legend
#q_filter {
border: 0 none;
color: #AAAAAA;
margin-bottom: -4px;
margin-top: -4px;
padding-left: 3px;
}
#node_filter {
border: 0px solid #545454;
\ No newline at end of file
/*README STYLE*/
div.readme {
padding:0px;
div.readme h2 {
font-weight: normal;
div.readme .readme_box {
background-color: #fafafa;
clear:both;
overflow:hidden;
margin:0;
padding:0 20px 10px;
div.readme .readme_box h1, div.readme .readme_box h2, div.readme .readme_box h3, div.readme .readme_box h4, div.readme .readme_box h5, div.readme .readme_box h6 {
border-bottom: 0 !important;
margin: 0 !important;
padding: 0 !important;
line-height: 1.5em !important;
div.readme .readme_box h1:first-child {
padding-top: .25em !important;
div.readme .readme_box h2, div.readme .readme_box h3 {
margin: 1em 0 !important;
div.readme .readme_box h2 {
margin-top: 1.5em !important;
border-top: 4px solid #e0e0e0 !important;
padding-top: .5em !important;
div.readme .readme_box p {
color: black !important;
div.readme .readme_box ul {
list-style: disc !important;
margin: 1em 0 1em 2em !important;
div.readme .readme_box ol {
list-style: decimal;
div.readme .readme_box pre, code {
font: 12px "Bitstream Vera Sans Mono","Courier",monospace;
div.readme .readme_box code {
font-size: 12px !important;
background-color: ghostWhite !important;
color: #444 !important;
padding: 0 .2em !important;
border: 1px solid #dedede !important;
div.readme .readme_box pre code {
background-color: #eee !important;
border: none !important;
div.readme .readme_box pre {
margin: 1em 0;
font-size: 12px;
background-color: #eee;
border: 1px solid #ddd;
padding: 5px;
color: #444;
overflow: auto;
-webkit-box-shadow: rgba(0,0,0,0.07) 0 1px 2px inset;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
@@ -55,80 +55,71 @@
%if c.dbrepo.private:
<img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="${h.url('/images/icons/lock.png')}"/>
%else:
<img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="${h.url('/images/icons/lock_open.png')}"/>
%endif
##REPO NAME
<span class="repo_name">${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')}"/>
<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')}"/>
<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">
<label>${_('Description')}:</label>
<div class="input-short desc">${h.urlify_text(c.dbrepo.description)}</div>
<label>${_('Contact')}:</label>
<div class="input-short">
<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>
<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="${c.rhodecode_repo.alias} clone ${c.clone_repo_url}" size="70"/>
@@ -178,24 +169,80 @@
%if c.rhodecode_user.username != 'default':
${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.dbrepo.repo_name),class_='rss_icon')}
${h.link_to(_('Atom'),h.url('atom_feed_home',repo_name=c.dbrepo.repo_name),class_='atom_icon')}
<div class="box box-right" style="min-height:455px">
<!-- box / title -->
<div class="title">
<h5>${_('Commit activity by day / author')}</h5>
<div class="graph">
<div style="padding:0 10px 10px 15px;font-size: 1.2em;">
%if c.no_data:
${c.no_data_msg}
%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-button-small")}
${_('Loaded in')} ${c.stats_percentage} %
<div id="commit_history" style="width:450px;height:300px;float:left"></div>
<div style="clear: both;height: 10px"></div>
<div id="overview" style="width:450px;height:100px;float:left"></div>
<div id="legend_data" style="clear:both;margin-top:10px;">
<div id="legend_container"></div>
<div id="legend_choices">
<table id="legend_choices_tables" class="noborder" style="font-size:smaller;color:#545454"></table>
<div class="box">
<div class="breadcrumbs">${h.link_to(_('Shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}</div>
<div class="table">
<div id="shortlog_data">
<%include file='../shortlog/shortlog_data.html'/>
%if c.readme_data:
<div class="box" style="background-color: #FAFAFA">
<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">
YUE.onDOMReady(function(e){
id = 'clone_url';
YUE.on(id,'click',function(e){
if(YUD.hasClass(id,'selected')){
return
else{
YUD.addClass(id,'selected');
YUD.get(id).select();
@@ -295,54 +342,24 @@
for(k in tmpl_links){
var s = YUD.get(k+'_link');
title_tmpl = "${_('Download %s as %s') % ('__CS_NAME__','__CS_EXT__')}";
s.title = title_tmpl.replace('__CS_NAME__',new_cs.text);
s.title = s.title.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);
s.innerHTML = url
});
</script>
<table id="legend_choices_tables" style="font-size:smaller;color:#545454"></table>
/**
* Plots summary graph
*
* @class SummaryPlot
* @param {from} initial from for detailed graph
* @param {to} initial to for detailed graph
* @param {dataset}
* @param {overview_dataset}
*/
function SummaryPlot(from,to,dataset,overview_dataset) {
var initial_ranges = {
@@ -649,52 +666,13 @@
plot.subscribe("plotselected", plotselected);
plot.subscribe("plothover", plothover)
overview.subscribe("plotselected", function (ranges) {
plot.setSelection(ranges);
YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});
##%if c.repo_changesets:
## ${h.link_to(_('show more'),h.url('changelog_home',repo_name=c.repo_name))}
##%endif
<div class="breadcrumbs">${h.link_to(_('Tags'),h.url('tags_home',repo_name=c.repo_name))}</div>
<%include file='../tags/tags_data.html'/>
%if c.repo_changesets:
${h.link_to(_('show more'),h.url('tags_home',repo_name=c.repo_name))}
<div class="breadcrumbs">${h.link_to(_('Branches'),h.url('branches_home',repo_name=c.repo_name))}</div>
<%include file='../branches/branches_data.html'/>
${h.link_to(_('show more'),h.url('branches_home',repo_name=c.repo_name))}
</%def>
@@ -15,25 +15,27 @@ requirements = [
"WebHelpers>=1.2",
"formencode==1.2.4",
"SQLAlchemy==0.7.3",
"Mako==0.5.0",
"pygments>=1.4",
"mercurial>=1.9.3,<2.0",
"whoosh<1.8",
"celery>=2.2.5,<2.3",
"babel",
"python-dateutil>=1.5.0,<2.0.0",
"dulwich>=0.8.0,<0.9.0",
"vcs>=0.2.3.dev",
"webob==1.0.8"
"webob==1.0.8",
"markdown==2.0.3",
"docutils==0.8.1",
dependency_links = [
"https://secure.rhodecode.org/vcs/archive/default.zip#egg=vcs-0.2.3.dev",
"https://bitbucket.org/marcinkuzminski/vcs/get/default.zip#egg=vcs-0.2.3.dev",
classifiers = ['Development Status :: 4 - Beta',
'Environment :: Web Environment',
'Framework :: Pylons',
'Intended Audience :: Developers',
'License :: OSI Approved :: GNU General Public License (GPL)',
Status change: