diff --git a/rhodecode/lib/helpers.py b/rhodecode/lib/helpers.py
--- a/rhodecode/lib/helpers.py
+++ b/rhodecode/lib/helpers.py
@@ -928,52 +928,67 @@ def gravatar_with_user(request, author,
return _render('gravatar_with_user', author, show_disabled=show_disabled)
-def desc_stylize(value):
+tags_paterns = OrderedDict((
+ ('lang', (re.compile(r'\[(lang|language)\ \=\>\ *([a-zA-Z\-\/\#\+\.]*)\]'),
+ '
\\2
')),
+
+ ('see', (re.compile(r'\[see\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]'),
+ 'see => \\1
')),
+
+ ('url', (re.compile(r'\[url\ \=\>\ \[([a-zA-Z0-9\ \.\-\_]+)\]\((.*?)\)\]'),
+ '')),
+
+ ('license', (re.compile(r'\[license\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]'),
+ '')),
+
+ ('ref', (re.compile(r'\[(requires|recommends|conflicts|base)\ \=\>\ *([a-zA-Z0-9\-\/]*)\]'),
+ '')),
+
+ ('state', (re.compile(r'\[(stable|featured|stale|dead|dev)\]'),
+ '\\1
')),
+
+ # label in grey
+ ('label', (re.compile(r'\[([a-z]+)\]'),
+ '\\1
')),
+
+ # generic catch all in grey
+ ('generic', (re.compile(r'\[([a-zA-Z0-9\.\-\_]+)\]'),
+ '\\1
')),
+))
+
+
+def extract_metatags(value):
"""
- converts tags from value into html equivalent
-
- :param value:
+ Extract supported meta-tags from given text value
"""
if not value:
return ''
- value = re.sub(r'\[see\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
- 'see => \\1
', value)
- value = re.sub(r'\[license\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
- '', value)
- value = re.sub(r'\[(requires|recommends|conflicts|base)\ \=\>\ *([a-zA-Z0-9\-\/]*)\]',
- '', value)
- value = re.sub(r'\[(lang|language)\ \=\>\ *([a-zA-Z\-\/\#\+]*)\]',
- '\\2
', value)
- value = re.sub(r'\[([a-z]+)\]',
- '\\1
', value)
+ tags = []
+ for key, val in tags_paterns.items():
+ pat, replace_html = val
+ tags.extend([(key, x.group()) for x in pat.finditer(value)])
+ value = pat.sub('', value)
- return value
+ return tags, value
-def escaped_stylize(value):
+def style_metatag(tag_type, value):
"""
- converts tags from value into html equivalent, but escaping its value first
+ converts tags from value into html equivalent
"""
if not value:
return ''
- # Using default webhelper escape method, but has to force it as a
- # plain unicode instead of a markup tag to be used in regex expressions
- value = unicode(escape(safe_unicode(value)))
+ html_value = value
+ tag_data = tags_paterns.get(tag_type)
+ if tag_data:
+ pat, replace_html = tag_data
+ # convert to plain `unicode` instead of a markup tag to be used in
+ # regex expressions. safe_unicode doesn't work here
+ html_value = pat.sub(replace_html, unicode(value))
- value = re.sub(r'\[see\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
- 'see => \\1
', value)
- value = re.sub(r'\[license\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
- '', value)
- value = re.sub(r'\[(requires|recommends|conflicts|base)\ \=\>\ *([a-zA-Z0-9\-\/]*)\]',
- '', value)
- value = re.sub(r'\[(lang|language)\ \=\>\ *([a-zA-Z\-\/\#\+]*)\]',
- '\\2
', value)
- value = re.sub(r'\[([a-z]+)\]',
- '\\1
', value)
-
- return value
+ return html_value
def bool2icon(value):
diff --git a/rhodecode/model/repo.py b/rhodecode/model/repo.py
--- a/rhodecode/model/repo.py
+++ b/rhodecode/model/repo.py
@@ -220,12 +220,7 @@ class RepoModel(BaseModel):
cs_cache.get('message'))
def desc(desc):
- if c.visual.stylify_metatags:
- desc = h.urlify_text(h.escaped_stylize(desc))
- else:
- desc = h.urlify_text(h.html_escape(desc))
-
- return _render('repo_desc', desc)
+ return _render('repo_desc', desc, c.visual.stylify_metatags)
def state(repo_state):
return _render("repo_state", repo_state)
diff --git a/rhodecode/model/repo_group.py b/rhodecode/model/repo_group.py
--- a/rhodecode/model/repo_group.py
+++ b/rhodecode/model/repo_group.py
@@ -694,14 +694,8 @@ class RepoGroupModel(BaseModel):
return _render("last_change", last_change)
def desc(desc, personal):
- prefix = h.escaped_stylize(u'[personal] ') if personal else ''
-
- if c.visual.stylify_metatags:
- desc = h.urlify_text(prefix + h.escaped_stylize(desc))
- else:
- desc = h.urlify_text(prefix + h.html_escape(desc))
-
- return _render('repo_group_desc', desc)
+ return _render(
+ 'repo_group_desc', desc, personal, c.visual.stylify_metatags)
def repo_group_actions(repo_group_id, repo_group_name, gr_count):
return _render(
diff --git a/rhodecode/public/css/tags.less b/rhodecode/public/css/tags.less
--- a/rhodecode/public/css/tags.less
+++ b/rhodecode/public/css/tags.less
@@ -71,14 +71,31 @@
}
}
-[tag="featured"] { &:extend(.tag1); }
-[tag="stale"] { &:extend(.tag2); }
-[tag="dead"] { &:extend(.tag3); }
-[tag="lang"] { &:extend(.tag4); }
-[tag="license"] { &:extend(.tag5); }
-[tag="requires"] { &:extend(.tag6); }
-[tag="recommends"] { &:extend(.tag7); }
+[tag="generic"] { &:extend(.tag0); }
+[tag="label"] { &:extend(.tag0); }
+
+[tag="state featured"] { &:extend(.tag1); }
+[tag="state dev"] { &:extend(.tag1); }
+[tag="ref base"] { &:extend(.tag1); }
+
+[tag="state stable"] { &:extend(.tag2); }
+[tag="state stale"] { &:extend(.tag2); }
+
+[tag="ref requires"] { &:extend(.tag3); }
+
+[tag="state dead"] { &:extend(.tag4); }
+
+[tag="ref conflicts"] { &:extend(.tag4); }
+
+[tag="license"] { &:extend(.tag6); }
+
+[tag="lang"] { &:extend(.tag7); }
+[tag="language"] { &:extend(.tag7); }
+[tag="ref recommends"] { &:extend(.tag7); }
+
[tag="see"] { &:extend(.tag8); }
+[tag="url"] { &:extend(.tag8); }
+
.perm_overriden {
text-decoration: line-through;
diff --git a/rhodecode/templates/admin/repo_groups/repo_group_add.mako b/rhodecode/templates/admin/repo_groups/repo_group_add.mako
--- a/rhodecode/templates/admin/repo_groups/repo_group_add.mako
+++ b/rhodecode/templates/admin/repo_groups/repo_group_add.mako
@@ -46,6 +46,12 @@
${h.textarea('group_description',cols=23,rows=5,class_="medium")}
+ <% metatags_url = h.literal('''
meta-tags ''') %>
+
${_('Plain text format with support of {metatags}').format(metatags=metatags_url)|n}
+
+ <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
+ ${dt.metatags_help()}
+
diff --git a/rhodecode/templates/admin/repo_groups/repo_group_edit_settings.mako b/rhodecode/templates/admin/repo_groups/repo_group_edit_settings.mako
--- a/rhodecode/templates/admin/repo_groups/repo_group_edit_settings.mako
+++ b/rhodecode/templates/admin/repo_groups/repo_group_edit_settings.mako
@@ -43,6 +43,12 @@
${h.textarea('group_description',cols=23,rows=5,class_="medium")}
+ <% metatags_url = h.literal('''
meta-tags ''') %>
+
${_('Plain text format with support of {metatags}').format(metatags=metatags_url)|n}
+
+ <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
+ ${dt.metatags_help()}
+
diff --git a/rhodecode/templates/admin/repos/repo_add_base.mako b/rhodecode/templates/admin/repos/repo_add_base.mako
--- a/rhodecode/templates/admin/repos/repo_add_base.mako
+++ b/rhodecode/templates/admin/repos/repo_add_base.mako
@@ -44,7 +44,12 @@
${h.textarea('repo_description')}
-
${_('Keep it short and to the point. Use a README file for longer descriptions.')}
+ <% metatags_url = h.literal('''
meta-tags ''') %>
+
${_('Plain text format with support of {metatags}. Add a README file for longer descriptions').format(metatags=metatags_url)|n}
+
+ <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
+ ${dt.metatags_help()}
+
diff --git a/rhodecode/templates/admin/repos/repo_edit_settings.mako b/rhodecode/templates/admin/repos/repo_edit_settings.mako
--- a/rhodecode/templates/admin/repos/repo_edit_settings.mako
+++ b/rhodecode/templates/admin/repos/repo_edit_settings.mako
@@ -129,7 +129,13 @@
${c.form['repo_description'].render(css_class='medium', oid='repo_description')|n}
${c.form.render_error(request, c.form['repo_description'])|n}
-
${_('Keep it short and to the point. Use a README file for longer descriptions.')}
+
+ <% metatags_url = h.literal('''
meta-tags ''') %>
+
${_('Plain text format with support of {metatags}. Add a README file for longer descriptions').format(metatags=metatags_url)|n}
+
+ <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
+ ${dt.metatags_help()}
+
diff --git a/rhodecode/templates/admin/settings/settings_visual.mako b/rhodecode/templates/admin/settings/settings_visual.mako
--- a/rhodecode/templates/admin/settings/settings_visual.mako
+++ b/rhodecode/templates/admin/settings/settings_visual.mako
@@ -63,21 +63,10 @@
${h.checkbox('rhodecode_stylify_metatags','True')}
${_('Stylify recognised meta tags')}
- ${_('Parses meta tags from repository description field and turns them into colored tags.')}
+ ${_('Parses meta tags from repository or repository group description fields and turns them into colored tags.')}
-
- [featured] featured
- [stale] stale
- [dead] dead
- [personal] personal
-
- [lang => lang] lang
-
- [license => License] License
- [requires => Repo] requires => Repo
- [recommends => Repo] recommends => Repo
- [see => URI] see => URI
-
+ <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
+ ${dt.metatags_help()}
diff --git a/rhodecode/templates/data_table/_dt_elements.mako b/rhodecode/templates/data_table/_dt_elements.mako
--- a/rhodecode/templates/data_table/_dt_elements.mako
+++ b/rhodecode/templates/data_table/_dt_elements.mako
@@ -3,6 +3,39 @@
## <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
<%namespace name="base" file="/base/base.mako"/>
+<%def name="metatags_help()">
+
+ <%
+ example_tags = [
+ ('state','[stable]'),
+ ('state','[stale]'),
+ ('state','[featured]'),
+ ('state','[dev]'),
+ ('state','[dead]'),
+
+ ('label','[personal]'),
+ ('generic','[v2.0.0]'),
+
+ ('lang','[lang => JavaScript]'),
+ ('license','[license => LicenseName]'),
+
+ ('ref','[requires => RepoName]'),
+ ('ref','[recommends => GroupName]'),
+ ('ref','[conflicts => SomeName]'),
+ ('ref','[base => SomeName]'),
+ ('url','[url => [linkName](https://rhodecode.com)]'),
+ ('see','[see => http://rhodecode.com]'),
+ ]
+ %>
+ % for tag_type, tag in example_tags:
+
+ ${tag|n}
+ ${h.style_metatag(tag_type, tag)|n}
+
+ % endfor
+
+%def>
+
## REPOSITORY RENDERERS
<%def name="quick_menu(repo_name)">
@@ -74,8 +107,20 @@
%def>
-<%def name="repo_desc(description)">
- ${description}
+<%def name="repo_desc(description, stylify_metatags)">
+ <%
+ tags, description = h.extract_metatags(description)
+ %>
+
+
+ % if stylify_metatags:
+ % for tag_type, tag in tags:
+ ${h.style_metatag(tag_type, tag)|n}
+ % endfor
+ % endif
+ ${description}
+
+
%def>
<%def name="last_change(last_change)">
@@ -168,8 +213,25 @@
%def>
-<%def name="repo_group_desc(description)">
- ${description}
+<%def name="repo_group_desc(description, personal, stylify_metatags)">
+
+ <%
+ tags, description = h.extract_metatags(description)
+ %>
+
+
+ % if personal:
+
${_('personal')}
+ % endif
+
+ % if stylify_metatags:
+ % for tag_type, tag in tags:
+ ${h.style_metatag(tag_type, tag)|n}
+ % endfor
+ % endif
+ ${description}
+
+
%def>
<%def name="repo_group_actions(repo_group_id, repo_group_name, gr_count)">
diff --git a/rhodecode/templates/forks/fork.mako b/rhodecode/templates/forks/fork.mako
--- a/rhodecode/templates/forks/fork.mako
+++ b/rhodecode/templates/forks/fork.mako
@@ -49,7 +49,12 @@
${h.textarea('description')}
-
${_('Keep it short and to the point. Use a README file for longer descriptions.')}
+ <% metatags_url = h.literal('''
meta-tags ''') %>
+
${_('Plain text format with support of {metatags}. Add a README file for longer descriptions').format(metatags=metatags_url)|n}
+
+ <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
+ ${dt.metatags_help()}
+
diff --git a/rhodecode/templates/summary/components.mako b/rhodecode/templates/summary/components.mako
--- a/rhodecode/templates/summary/components.mako
+++ b/rhodecode/templates/summary/components.mako
@@ -82,11 +82,10 @@
${_('Description')}:
- %if c.visual.stylify_metatags:
-
${h.urlify_text(h.escaped_stylize(c.rhodecode_db_repo.description))}
- %else:
-
${h.urlify_text(h.html_escape(c.rhodecode_db_repo.description))}
- %endif
+
+ <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
+ ${dt.repo_desc(c.rhodecode_db_repo.description_safe, c.visual.stylify_metatags)}
+
diff --git a/rhodecode/tests/lib/test_libs.py b/rhodecode/tests/lib/test_libs.py
--- a/rhodecode/tests/lib/test_libs.py
+++ b/rhodecode/tests/lib/test_libs.py
@@ -186,31 +186,115 @@ def test_age_in_future(age_args, expecte
assert translate(age(n + delt(**age_args), now=n, **kw)) == expected
-def test_tag_exctrator():
- sample = (
- "hello pta[tag] gog [[]] [[] sda ero[or]d [me =>>< sa]"
- "[requires] [stale] [see<>=>] [see => http://url.com]"
- "[requires => url] [lang => python] [just a tag] "
- "[,d] [ => ULR ] [obsolete] [desc]]"
- )
- from rhodecode.lib.helpers import desc_stylize, escaped_stylize
- res = desc_stylize(sample)
- assert 'tag
' in res
- assert 'obsolete
' in res
- assert 'stale
' in res
- assert 'python
' in res
- assert '' in res
- assert 'tag
' in res
- assert ' ' in res
+@pytest.mark.parametrize("sample, expected_tags", [
+ ((
+ "hello world [stale]"
+ ),
+ [
+ ('state', '[stale]'),
+ ]),
+ # entry
+ ((
+ "hello world [v2.0.0] [v1.0.0]"
+ ),
+ [
+ ('generic', '[v2.0.0]'),
+ ('generic', '[v1.0.0]'),
+ ]),
+ # entry
+ ((
+ "he[ll]o wo[rl]d"
+ ),
+ [
+ ('label', '[ll]'),
+ ('label', '[rl]'),
+ ]),
+ # entry
+ ((
+ "hello world [stale]\n[featured]\n[stale] [dead] [dev]"
+ ),
+ [
+ ('state', '[stale]'),
+ ('state', '[featured]'),
+ ('state', '[stale]'),
+ ('state', '[dead]'),
+ ('state', '[dev]'),
+ ]),
+ # entry
+ ((
+ "hello world \n\n [stale] \n [url => [name](http://rc.com)]"
+ ),
+ [
+ ('state', '[stale]'),
+ ('url', '[url => [name](http://rc.com)]'),
+ ]),
+ # entry
+ ((
+ "hello pta[tag] gog [[]] [[] sda ero[or]d [me =>>< sa]"
+ "[requires] [stale] [see<>=>] [see => http://url.com]"
+ "[requires => url] [lang => python] [just a tag] "
+ " "
+ "[,d] [ => ULR ] [obsolete] [desc]]"
+ ),
+ [
+ ('label', '[desc]'),
+ ('label', '[obsolete]'),
+ ('label', '[or]'),
+ ('label', '[requires]'),
+ ('label', '[tag]'),
+ ('state', '[stale]'),
+ ('lang', '[lang => python]'),
+ ('ref', '[requires => url]'),
+ ('see', '[see => http://url.com]'),
- res_encoded = escaped_stylize(sample)
- assert 'tag
' in res_encoded
- assert 'obsolete
' in res_encoded
- assert 'stale
' in res_encoded
- assert 'python
' in res_encoded
- assert '' in res_encoded
- assert 'tag
' in res_encoded
- assert '<html_tag first='abc' attr="my.url?attr=&another="></html_tag>' in res_encoded
+ ]),
+
+], ids=no_newline_id_generator)
+def test_metatag_extraction(sample, expected_tags):
+ from rhodecode.lib.helpers import extract_metatags
+ tags, value = extract_metatags(sample)
+ assert sorted(tags) == sorted(expected_tags)
+
+
+@pytest.mark.parametrize("tag_data, expected_html", [
+
+ (('state', '[stable]'), 'stable
'),
+ (('state', '[stale]'), 'stale
'),
+ (('state', '[featured]'), 'featured
'),
+ (('state', '[dev]'), 'dev
'),
+ (('state', '[dead]'), 'dead
'),
+
+ (('label', '[personal]'), 'personal
'),
+ (('generic', '[v2.0.0]'), 'v2.0.0
'),
+
+ (('lang', '[lang => JavaScript]'), 'JavaScript
'),
+ (('lang', '[lang => C++]'), 'C++
'),
+ (('lang', '[lang => C#]'), 'C#
'),
+ (('lang', '[lang => Delphi/Object]'), 'Delphi/Object
'),
+ (('lang', '[lang => Objective-C]'), 'Objective-C
'),
+ (('lang', '[lang => .NET]'), '.NET
'),
+
+ (('license', '[license => BSD 3-clause]'), ''),
+ (('license', '[license => GPLv3]'), ''),
+ (('license', '[license => MIT]'), ''),
+ (('license', '[license => AGPLv3]'), ''),
+
+ (('ref', '[requires => RepoName]'), ''),
+ (('ref', '[recommends => GroupName]'), ''),
+ (('ref', '[conflicts => SomeName]'), ''),
+ (('ref', '[base => SomeName]'), ''),
+
+ (('see', '[see => http://rhodecode.com]'), 'see => http://rhodecode.com
'),
+
+ (('url', '[url => [linkName](https://rhodecode.com)]'), ''),
+ (('url', '[url => [example link](https://rhodecode.com)]'), ''),
+ (('url', '[url => [v1.0.0](https://rhodecode.com)]'), ''),
+
+])
+def test_metatags_stylize(tag_data, expected_html):
+ from rhodecode.lib.helpers import style_metatag
+ tag_type,value = tag_data
+ assert style_metatag(tag_type, value) == expected_html
@pytest.mark.parametrize("tmpl_url, email, expected", [