# -*- coding: utf-8 -*-
"""
rhodecode.lib.markup_renderer
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Renderer for markup languages with ability to parse using rst or markdown
:created_on: Oct 27, 2011
:author: marcink
:copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
:license: GPLv3, see COPYING for more details.
# 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, 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 re
import logging
import traceback
from rhodecode.lib.utils2 import safe_unicode, MENTIONS_REGEX
log = logging.getLogger(__name__)
class MarkupRenderer(object):
RESTRUCTUREDTEXT_DISALLOWED_DIRECTIVES = ['include', 'meta', 'raw']
MARKDOWN_PAT = re.compile(r'md|mkdn?|mdown|markdown', re.IGNORECASE)
RST_PAT = re.compile(r're?st', re.IGNORECASE)
PLAIN_PAT = re.compile(r'readme', re.IGNORECASE)
def __detect_renderer(self, source, filename=None):
runs detection of what renderer should be used for generating html
from a markup language
filename can be also explicitly a renderer name
:param source:
:param filename:
if MarkupRenderer.MARKDOWN_PAT.findall(filename):
detected_renderer = 'markdown'
elif MarkupRenderer.RST_PAT.findall(filename):
detected_renderer = 'rst'
elif MarkupRenderer.PLAIN_PAT.findall(filename):
else:
detected_renderer = 'plain'
return getattr(MarkupRenderer, detected_renderer)
def render(self, source, filename=None):
Renders a given filename using detected renderer
it detects renderers based on file extension or mimetype.
At last it will just do a simple html replacing new lines with <br/>
:param file_name:
renderer = self.__detect_renderer(source, filename)
readme_data = renderer(source)
return readme_data
@classmethod
def plain(cls, source):
source = safe_unicode(source)
def urlify_text(text):
url_pat = re.compile(r'(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]'
'|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)')
def url_func(match_obj):
url_full = match_obj.groups()[0]
return '<a href="%(url)s">%(url)s</a>' % ({'url': url_full})
return url_pat.sub(url_func, text)
source = urlify_text(source)
return '<br />' + source.replace("\n", '<br />')
def markdown(cls, source, safe=True):
try:
import markdown as __markdown
return __markdown.markdown(source, ['codehilite'])
return __markdown.markdown(source, ['codehilite', 'tables'])
except ImportError:
log.warning('Install markdown to use this function')
return cls.plain(source)
except Exception:
log.error(traceback.format_exc())
if safe:
return source
raise
def rst(cls, source, safe=True):
from docutils.core import publish_parts
from docutils.parsers.rst import directives
docutils_settings = dict([(alias, None) for alias in
cls.RESTRUCTUREDTEXT_DISALLOWED_DIRECTIVES])
docutils_settings.update({'input_encoding': 'unicode',
'report_level': 4})
for k, v in docutils_settings.iteritems():
directives.register_directive(k, v)
parts = publish_parts(source=source,
writer_name="html4css1",
settings_overrides=docutils_settings)
return parts['html_title'] + parts["fragment"]
log.warning('Install docutils to use this function')
def rst_with_mentions(cls, source):
mention_pat = re.compile(MENTIONS_REGEX)
def wrapp(match_obj):
uname = match_obj.groups()[0]
return ' **@%(uname)s** ' % {'uname': uname}
mention_hl = mention_pat.sub(wrapp, source).strip()
return cls.rst(mention_hl)
@@ -3711,768 +3711,776 @@ div#legend_data,div#legend_container,div
#content div.box div.form div.fields div.field div.select a:hover,#content div.box div.form div.fields div.field div.select a.ui-selectmenu:hover,#content div.box div.action a:hover
{
color: #000;
text-decoration: none;
}
#content div.box div.form div.fields div.field div.select a.ui-selectmenu-focus,#content div.box div.action a.ui-selectmenu-focus
border: 1px solid #666;
#content div.box div.form div.fields div.field div.checkboxes div.checkbox,#content div.box div.form div.fields div.field div.radios div.radio
clear: both;
overflow: hidden;
margin: 0;
padding: 8px 0 2px;
#content div.box div.form div.fields div.field div.checkboxes div.checkbox input,#content div.box div.form div.fields div.field div.radios div.radio input
float: left;
#content div.box div.form div.fields div.field div.checkboxes div.checkbox label,#content div.box div.form div.fields div.field div.radios div.radio label
height: 1%;
display: block;
margin: 2px 0 0 4px;
div.form div.fields div.field div.button input,
#content div.box div.form div.fields div.buttons input
div.form div.fields div.buttons input,
#content div.box div.action div.button input {
/*color: #000;*/
font-size: 11px;
font-weight: 700;
input.ui-button {
background: #e5e3e3 url("../images/button.png") repeat-x;
border-top: 1px solid #DDD;
border-left: 1px solid #c6c6c6;
border-right: 1px solid #DDD;
border-bottom: 1px solid #c6c6c6;
color: #515151 !important;
outline: none;
padding: 6px 12px;
-webkit-border-radius: 4px 4px 4px 4px;
-khtml-border-radius: 4px 4px 4px 4px;
-moz-border-radius: 4px 4px 4px 4px;
border-radius: 4px 4px 4px 4px;
box-shadow: 0 1px 0 #ececec;
cursor: pointer;
input.ui-button:hover {
background: #b4b4b4 url("../images/button_selected.png") repeat-x;
border-top: 1px solid #ccc;
border-left: 1px solid #bebebe;
border-right: 1px solid #b1b1b1;
border-bottom: 1px solid #afafaf;
div.form div.fields div.field div.highlight,#content div.box div.form div.fields div.buttons div.highlight
display: inline;
#content div.box div.form div.fields div.buttons,div.form div.fields div.buttons
margin: 10px 0 0 200px;
padding: 0;
#content div.box-left div.form div.fields div.buttons,#content div.box-right div.form div.fields div.buttons,div.box-left div.form div.fields div.buttons,div.box-right div.form div.fields div.buttons
margin: 10px 0 0;
#content div.box table td.user,#content div.box table td.address {
width: 10%;
text-align: center;
#content div.box div.action div.button,#login div.form div.fields div.field div.input div.link,#register div.form div.fields div.field div.input div.link
text-align: right;
margin: 6px 0 0;
#content div.box div.action div.button input.ui-state-hover,#login div.form div.fields div.buttons input.ui-state-hover,#register div.form div.fields div.buttons input.ui-state-hover
color: #515151;
#content div.box div.pagination div.results,#content div.box div.pagination-wh div.results
text-align: left;
#content div.box div.pagination div.results span,#content div.box div.pagination-wh div.results span
background: #ebebeb url("../images/pager.png") repeat-x;
border-top: 1px solid #dedede;
border-left: 1px solid #cfcfcf;
border-right: 1px solid #c4c4c4;
border-bottom: 1px solid #c4c4c4;
color: #4A4A4A;
padding: 6px 8px;
#content div.box div.pagination ul.pager li.disabled,#content div.box div.pagination-wh a.disabled
color: #B4B4B4;
padding: 6px;
#login,#register {
width: 520px;
margin: 10% auto 0;
#login div.color,#register div.color {
background: #FFF;
margin: 10px auto 0;
padding: 3px 3px 3px 0;
#login div.color a,#register div.color a {
width: 20px;
height: 20px;
margin: 0 0 0 3px;
#login div.title h5,#register div.title h5 {
color: #fff;
margin: 10px;
#login div.form div.fields div.field,#register div.form div.fields div.field
padding: 0 0 10px;
#login div.form div.fields div.field span.error-message,#register div.form div.fields div.field span.error-message
color: red;
margin: 8px 0 0;
max-width: 320px;
#login div.form div.fields div.field div.label label,#register div.form div.fields div.field div.label label
#login div.form div.fields div.field div.input,#register div.form div.fields div.field div.input
#login div.form div.fields div.field div.checkbox,#register div.form div.fields div.field div.checkbox
margin: 0 0 0 184px;
#login div.form div.fields div.field div.checkbox label,#register div.form div.fields div.field div.checkbox label
color: #565656;
#login div.form div.fields div.buttons input,#register div.form div.fields div.buttons input
font-size: 1em;
#changeset_content .container .wrapper,#graph_content .container .wrapper
width: 600px;
#changeset_content .container .left {
width: 75%;
padding-left: 5px;
#changeset_content .container .left .date,.ac .match {
padding-top: 5px;
padding-bottom: 5px;
div#legend_container table td,div#legend_choices table td {
border: none !important;
height: 20px !important;
padding: 0 !important;
.q_filter_box {
-webkit-box-shadow: rgba(0,0,0,0.07) 0 1px 2px inset;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
border: 0 none;
color: #AAAAAA;
margin-bottom: -4px;
margin-top: -4px;
padding-left: 3px;
#node_filter {
border: 0px solid #545454;
.group_members_wrap{
min-height: 85px;
padding-left: 20px;
.group_members .group_member{
height: 30px;
padding:0px 0px 0px 0px;
.reviewers_member{
height: 15px;
padding:0px 0px 0px 10px;
.emails_wrap{
padding: 0px 20px;
.emails_wrap .email_entry{
.emails_wrap .email_entry .email{
float: left
.emails_wrap .email_entry .email_action{
/*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;
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;
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-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
div.readme .readme_box table {
display: table;
border-collapse: separate;
border-spacing: 2px;
border-color: gray;
width: auto !important;
/** RST STYLE **/
div.rst-block {
div.rst-block h2 {
div.rst-block h1, div.rst-block h2, div.rst-block h3, div.rst-block h4, div.rst-block h5, div.rst-block h6 {
div.rst-block h1:first-child {
div.rst-block h2, div.rst-block h3 {
div.rst-block p {
div.rst-block ul {
div.rst-block ol {
div.rst-block pre, code {
div.rst-block code {
div.rst-block pre code {
div.rst-block pre {
/** comment main **/
.comments {
padding:10px 20px;
.comments .comment {
margin-top: 10px;
.comments .comment .meta {
background: #f8f8f8;
padding: 4px;
border-bottom: 1px solid #ddd;
height: 18px;
.comments .comment .meta img {
vertical-align: middle;
.comments .comment .meta .user {
font-weight: bold;
padding: 4px 2px 2px 2px;
.comments .comment .meta .date {
padding:4px 4px 0px 4px;
.comments .comment .text {
background-color: #FAFAFA;
.comment .text div.rst-block p {
margin: 0.5em 0px !important;
.comments .comments-number{
padding:0px 0px 10px 0px;
color: #666;
font-size: 16px;
/** comment form **/
.status-block{
height:80px;
clear:both
.comment-form .clearfix{
background: #EEE;
padding: 10px;
div.comment-form {
margin-top: 20px;
.comment-form strong {
margin-bottom: 15px;
.comment-form textarea {
width: 100%;
height: 100px;
font-family: 'Monaco', 'Courier', 'Courier New', monospace;
form.comment-form {
margin-left: 10px;
.comment-form-submit {
margin-top: 5px;
margin-left: 525px;
.file-comments {
display: none;
.comment-form .comment {
.comment-form .comment-help{
padding: 0px 0px 5px 0px;
.comment-form .comment-button{
padding-top:5px;
.add-another-button {
margin-bottom: 10px;
.comment .buttons {
float: right;
padding:2px 2px 0px 0px;
.show-inline-comments{
position: relative;
top:1px
/** comment inline form **/
.comment-inline-form .overlay{
.comment-inline-form .overlay.submitting{
display:block;
background: none repeat scroll 0 0 white;
opacity: 0.5;
position: absolute;
vertical-align: top;
.comment-inline-form .overlay.submitting .overlay-text{
width:100%;
margin-top:5%;
.comment-inline-form .clearfix{
div.comment-inline-form {
padding:4px 0px 6px 0px;
tr.hl-comment{
/*
background-color: #FFFFCC !important;
*/
tr.hl-comment pre {
border-top: 2px solid #FFEE33;
border-left: 2px solid #FFEE33;
border-right: 2px solid #FFEE33;
.comment-inline-form strong {
.comment-inline-form textarea {
form.comment-inline-form {
.comment-inline-form-submit {
.comment-inline-form .comment {
.comment-inline-form .comment-help{
padding: 0px 0px 2px 0px;
color: #666666;
font-size: 10px;
.comment-inline-form .comment-button{
/** comment inline **/
.inline-comments {
.inline-comments div.rst-block {
padding:0 20px 0px;
.inline-comments .comment {
margin: 3px 3px 5px 5px;
.inline-comments .add-comment {
padding: 2px 4px 8px 5px;
.inline-comments .comment-wrapp{
padding:1px;
.inline-comments .comment .meta {
.inline-comments .comment .meta img {
.inline-comments .comment .meta .user {
float:left;
padding: 3px;
.inline-comments .comment .meta .date {
.inline-comments .comment .text {
.inline-comments .comments-number{
.inline-comments-button .add-comment{
margin:2px 0px 8px 5px !important
.notification-paginator{
padding: 0px 0px 4px 16px;
.notifications{
margin: 20px 0px 0px 0px;
width: 26px;
z-index: 1000;
.notifications a{
color:#888 !important;
Status change: