Show More
@@ -29,15 +29,16 b' import collections' | |||
|
29 | 29 | import os |
|
30 | 30 | import random |
|
31 | 31 | import hashlib |
|
32 | from io import StringIO | |
|
32 | import io | |
|
33 | 33 | import textwrap |
|
34 |
import urllib.request |
|
|
34 | import urllib.request | |
|
35 | import urllib.parse | |
|
36 | import urllib.error | |
|
35 | 37 | import math |
|
36 | 38 | import logging |
|
37 | 39 | import re |
|
38 | 40 | import time |
|
39 | 41 | import string |
|
40 | import hashlib | |
|
41 | 42 | import regex |
|
42 | 43 | from collections import OrderedDict |
|
43 | 44 | |
@@ -70,19 +71,22 b' from webhelpers2.html.tags import (' | |||
|
70 | 71 | form as insecure_form, |
|
71 | 72 | auto_discovery_link, checkbox, end_form, file, |
|
72 | 73 | hidden, image, javascript_link, link_to, link_to_if, link_to_unless, ol, |
|
73 |
|
|
|
74 | stylesheet_link, submit, text, password, textarea, | |
|
74 | 75 | ul, radio, Options) |
|
75 | 76 | |
|
76 | 77 | from webhelpers2.number import format_byte_size |
|
78 | # python3.11 backport fixes for webhelpers2 | |
|
79 | from rhodecode.lib._vendor.webhelpers_backports import raw_select | |
|
77 | 80 | |
|
78 | 81 | from rhodecode.lib.action_parser import action_parser |
|
79 | 82 | from rhodecode.lib.pagination import Page, RepoPage, SqlPage |
|
80 | 83 | from rhodecode.lib import ext_json |
|
81 | 84 | from rhodecode.lib.ext_json import json |
|
82 | from rhodecode.lib.str_utils import safe_bytes | |
|
85 | from rhodecode.lib.str_utils import safe_bytes, convert_special_chars | |
|
83 | 86 | from rhodecode.lib.utils import repo_name_slug, get_custom_lexer |
|
87 | from rhodecode.lib.str_utils import safe_str | |
|
84 | 88 | from rhodecode.lib.utils2 import ( |
|
85 | str2bool, safe_unicode, safe_str, | |
|
89 | str2bool, | |
|
86 | 90 | get_commit_safe, datetime_to_time, time_to_datetime, time_to_utcdatetime, |
|
87 | 91 | AttributeDict, safe_int, md5, md5_safe, get_host_info) |
|
88 | 92 | from rhodecode.lib.markup_renderer import MarkupRenderer, relative_links |
@@ -123,11 +127,11 b' def asset(path, ver=None, **kwargs):' | |||
|
123 | 127 | |
|
124 | 128 | |
|
125 | 129 | default_html_escape_table = { |
|
126 |
ord('&'): |
|
|
127 |
ord('<'): |
|
|
128 |
ord('>'): |
|
|
129 |
ord('"'): |
|
|
130 |
ord("'"): |
|
|
130 | ord('&'): '&', | |
|
131 | ord('<'): '<', | |
|
132 | ord('>'): '>', | |
|
133 | ord('"'): '"', | |
|
134 | ord("'"): ''', | |
|
131 | 135 | } |
|
132 | 136 | |
|
133 | 137 | |
@@ -265,14 +269,12 b' class _ToolTip(object):' | |||
|
265 | 269 | |
|
266 | 270 | tooltip = _ToolTip() |
|
267 | 271 | |
|
268 |
files_icon = |
|
|
272 | files_icon = '<i class="file-breadcrumb-copy tooltip icon-clipboard clipboard-action" data-clipboard-text="{}" title="Copy file path"></i>' | |
|
269 | 273 | |
|
270 | 274 | |
|
271 | 275 | def files_breadcrumbs(repo_name, repo_type, commit_id, file_path, landing_ref_name=None, at_ref=None, |
|
272 | 276 | limit_items=False, linkify_last_item=False, hide_last_item=False, |
|
273 | 277 | copy_path_icon=True): |
|
274 | if isinstance(file_path, str): | |
|
275 | file_path = safe_unicode(file_path) | |
|
276 | 278 | |
|
277 | 279 | if at_ref: |
|
278 | 280 | route_qry = {'at': at_ref} |
@@ -282,7 +284,7 b' def files_breadcrumbs(repo_name, repo_ty' | |||
|
282 | 284 | default_landing_ref = commit_id |
|
283 | 285 | |
|
284 | 286 | # first segment is a `HOME` link to repo files root location |
|
285 |
root_name = literal( |
|
|
287 | root_name = literal('<i class="icon-home"></i>') | |
|
286 | 288 | |
|
287 | 289 | url_segments = [ |
|
288 | 290 | link_to( |
@@ -345,7 +347,6 b' def files_breadcrumbs(repo_name, repo_ty' | |||
|
345 | 347 | |
|
346 | 348 | |
|
347 | 349 | def files_url_data(request): |
|
348 | import urllib.request, urllib.parse, urllib.error | |
|
349 | 350 | matchdict = request.matchdict |
|
350 | 351 | |
|
351 | 352 | if 'f_path' not in matchdict: |
@@ -426,17 +427,17 b' class CodeHtmlFormatter(HtmlFormatter):' | |||
|
426 | 427 | My code Html Formatter for source codes |
|
427 | 428 | """ |
|
428 | 429 | |
|
429 |
def wrap(self, source |
|
|
430 | def wrap(self, source): | |
|
430 | 431 | return self._wrap_div(self._wrap_pre(self._wrap_code(source))) |
|
431 | 432 | |
|
432 | 433 | def _wrap_code(self, source): |
|
433 | 434 | for cnt, it in enumerate(source): |
|
434 | 435 | i, t = it |
|
435 |
t = '<div id="L |
|
|
436 | t = f'<div id="L{cnt+1}">{t}</div>' | |
|
436 | 437 | yield i, t |
|
437 | 438 | |
|
438 | 439 | def _wrap_tablelinenos(self, inner): |
|
439 |
dummyoutfile = |
|
|
440 | dummyoutfile = io.StringIO() | |
|
440 | 441 | lncount = 0 |
|
441 | 442 | for t, line in inner: |
|
442 | 443 | if t: |
@@ -594,7 +595,7 b' def unique_color_generator(n=10000, satu' | |||
|
594 | 595 | h %= 1 |
|
595 | 596 | HSV_tuple = [h, saturation, lightness] |
|
596 | 597 | RGB_tuple = hsv_to_rgb(*HSV_tuple) |
|
597 |
yield |
|
|
598 | yield [str(int(x * 256)) for x in RGB_tuple] | |
|
598 | 599 | |
|
599 | 600 | |
|
600 | 601 | def color_hasher(n=10000, saturation=0.10, lightness=0.95): |
@@ -692,7 +693,7 b' class _Message(object):' | |||
|
692 | 693 | __unicode__ = __str__ |
|
693 | 694 | |
|
694 | 695 | def __html__(self): |
|
695 |
return escape(safe_ |
|
|
696 | return escape(safe_str(self.message)) | |
|
696 | 697 | |
|
697 | 698 | |
|
698 | 699 | class Flash(object): |
@@ -771,7 +772,7 b' class Flash(object):' | |||
|
771 | 772 | for message in messages: |
|
772 | 773 | payloads.append({ |
|
773 | 774 | 'message': { |
|
774 |
'message': |
|
|
775 | 'message': '{}'.format(message.message), | |
|
775 | 776 | 'level': message.category, |
|
776 | 777 | 'force': True, |
|
777 | 778 | 'subdata': message.sub_data |
@@ -816,8 +817,7 b' def hide_credentials(url):' | |||
|
816 | 817 | from rhodecode.lib.utils2 import credentials_filter |
|
817 | 818 | return credentials_filter(url) |
|
818 | 819 | |
|
819 | ||
|
820 | import pytz | |
|
820 | import zoneinfo | |
|
821 | 821 | import tzlocal |
|
822 | 822 | local_timezone = tzlocal.get_localzone() |
|
823 | 823 | |
@@ -829,9 +829,10 b' def get_timezone(datetime_iso, time_is_l' | |||
|
829 | 829 | if time_is_local and isinstance(datetime_iso, datetime) and not datetime_iso.tzinfo: |
|
830 | 830 | force_timezone = os.environ.get('RC_TIMEZONE', '') |
|
831 | 831 | if force_timezone: |
|
832 |
force_timezone = |
|
|
832 | force_timezone = zoneinfo.ZoneInfo(force_timezone) | |
|
833 | 833 | timezone = force_timezone or local_timezone |
|
834 | offset = timezone.localize(datetime_iso).strftime('%z') | |
|
834 | ||
|
835 | offset = datetime_iso.replace(tzinfo=timezone).strftime('%z') | |
|
835 | 836 | tzinfo = '{}:{}'.format(offset[:-2], offset[-2:]) |
|
836 | 837 | return tzinfo |
|
837 | 838 | |
@@ -883,9 +884,9 b' def format_date(date):' | |||
|
883 | 884 | |
|
884 | 885 | if date: |
|
885 | 886 | _fmt = "%a, %d %b %Y %H:%M:%S" |
|
886 |
return safe_ |
|
|
887 | ||
|
888 |
return |
|
|
887 | return safe_str(date.strftime(_fmt)) | |
|
888 | ||
|
889 | return "" | |
|
889 | 890 | |
|
890 | 891 | |
|
891 | 892 | class _RepoChecker(object): |
@@ -1021,7 +1022,8 b' def author_string(email):' | |||
|
1021 | 1022 | |
|
1022 | 1023 | def person_by_id(id_, show_attr="username_and_name"): |
|
1023 | 1024 | # attr to return from fetched user |
|
1024 | person_getter = lambda usr: getattr(usr, show_attr) | |
|
1025 | def person_getter(usr): | |
|
1026 | return getattr(usr, show_attr) | |
|
1025 | 1027 | |
|
1026 | 1028 | #maybe it's an ID ? |
|
1027 | 1029 | if str(id_).isdigit() or isinstance(id_, int): |
@@ -1074,7 +1076,7 b' def extract_metatags(value):' | |||
|
1074 | 1076 | if not value: |
|
1075 | 1077 | return tags, '' |
|
1076 | 1078 | |
|
1077 | for key, val in tags_paterns.items(): | |
|
1079 | for key, val in list(tags_paterns.items()): | |
|
1078 | 1080 | pat, replace_html = val |
|
1079 | 1081 | tags.extend([(key, x.group()) for x in pat.finditer(value)]) |
|
1080 | 1082 | value = pat.sub('', value) |
@@ -1093,8 +1095,8 b' def style_metatag(tag_type, value):' | |||
|
1093 | 1095 | tag_data = tags_paterns.get(tag_type) |
|
1094 | 1096 | if tag_data: |
|
1095 | 1097 | pat, replace_html = tag_data |
|
1096 |
# convert to plain ` |
|
|
1097 |
# regex expressions. safe_ |
|
|
1098 | # convert to plain `str` instead of a markup tag to be used in | |
|
1099 | # regex expressions. safe_str doesn't work here | |
|
1098 | 1100 | html_value = pat.sub(replace_html, value) |
|
1099 | 1101 | |
|
1100 | 1102 | return html_value |
@@ -1117,7 +1119,7 b' def bool2icon(value, show_at_false=True)' | |||
|
1117 | 1119 | |
|
1118 | 1120 | |
|
1119 | 1121 | def b64(inp): |
|
1120 | return base64.b64encode(inp) | |
|
1122 | return base64.b64encode(safe_bytes(inp)) | |
|
1121 | 1123 | |
|
1122 | 1124 | #============================================================================== |
|
1123 | 1125 | # PERMS |
@@ -1230,23 +1232,22 b' class InitialsGravatar(object):' | |||
|
1230 | 1232 | return color_bank[pos] |
|
1231 | 1233 | |
|
1232 | 1234 | def normalize_email(self, email_address): |
|
1233 | import unicodedata | |
|
1234 | 1235 | # default host used to fill in the fake/missing email |
|
1235 | 1236 | default_host = 'localhost' |
|
1236 | 1237 | |
|
1237 | 1238 | if not email_address: |
|
1238 |
email_address = ' |
|
|
1239 | ||
|
1240 |
email_address = safe_ |
|
|
1241 | ||
|
1242 |
if |
|
|
1243 |
email_address = |
|
|
1244 | ||
|
1245 |
if email_address.endswith( |
|
|
1246 |
email_address = |
|
|
1247 | ||
|
1248 |
email_address = |
|
|
1249 | .encode('ascii', 'ignore') | |
|
1239 | email_address = f'{User.DEFAULT_USER}@{default_host}' | |
|
1240 | ||
|
1241 | email_address = safe_str(email_address) | |
|
1242 | ||
|
1243 | if '@' not in email_address: | |
|
1244 | email_address = f'{email_address}@{default_host}' | |
|
1245 | ||
|
1246 | if email_address.endswith('@'): | |
|
1247 | email_address = f'{email_address}{default_host}' | |
|
1248 | ||
|
1249 | email_address = convert_special_chars(email_address) | |
|
1250 | ||
|
1250 | 1251 | return email_address |
|
1251 | 1252 | |
|
1252 | 1253 | def get_initials(self): |
@@ -1266,12 +1267,11 b' class InitialsGravatar(object):' | |||
|
1266 | 1267 | Function also normalizes the non-ascii characters to they ascii |
|
1267 | 1268 | representation, eg Δ => A |
|
1268 | 1269 | """ |
|
1269 | import unicodedata | |
|
1270 | 1270 | # replace non-ascii to ascii |
|
1271 | first_name = unicodedata.normalize( | |
|
1272 | 'NFKD', safe_unicode(self.first_name)).encode('ascii', 'ignore') | |
|
1273 | last_name = unicodedata.normalize( | |
|
1274 | 'NFKD', safe_unicode(self.last_name)).encode('ascii', 'ignore') | |
|
1271 | first_name = convert_special_chars(self.first_name) | |
|
1272 | last_name = convert_special_chars(self.last_name) | |
|
1273 | # multi word last names, Guido Von Rossum, we take the last part only | |
|
1274 | last_name = last_name.split(' ', 1)[-1] | |
|
1275 | 1275 | |
|
1276 | 1276 | # do NFKD encoding, and also make sure email has proper format |
|
1277 | 1277 | email_address = self.normalize_email(self.email_address) |
@@ -1286,9 +1286,9 b' class InitialsGravatar(object):' | |||
|
1286 | 1286 | else: |
|
1287 | 1287 | initials = [prefix[0], server[0]] |
|
1288 | 1288 | |
|
1289 | # then try to replace either first_name or last_name | |
|
1289 | # get first letter of first and last names to create initials | |
|
1290 | 1290 | fn_letter = (first_name or " ")[0].strip() |
|
1291 |
ln_letter = (last_name |
|
|
1291 | ln_letter = (last_name or " ")[0].strip() | |
|
1292 | 1292 | |
|
1293 | 1293 | if fn_letter: |
|
1294 | 1294 | initials[0] = fn_letter |
@@ -1305,6 +1305,7 b' class InitialsGravatar(object):' | |||
|
1305 | 1305 | viewBox="-15 -10 439.165 429.164" |
|
1306 | 1306 | |
|
1307 | 1307 | xml:space="preserve" |
|
1308 | font-family="{font_family} | |
|
1308 | 1309 | style="background:{background};" > |
|
1309 | 1310 | |
|
1310 | 1311 | <path d="M204.583,216.671c50.664,0,91.74-48.075, |
@@ -1374,8 +1375,8 b' class InitialsGravatar(object):' | |||
|
1374 | 1375 | return img_data |
|
1375 | 1376 | |
|
1376 | 1377 | def generate_svg(self, svg_type=None): |
|
1377 | img_data = self.get_img_data(svg_type) | |
|
1378 | return "data:image/svg+xml;base64,%s" % base64.b64encode(img_data) | |
|
1378 | img_data = safe_bytes(self.get_img_data(svg_type)) | |
|
1379 | return "data:image/svg+xml;base64,%s" % safe_str(base64.b64encode(img_data)) | |
|
1379 | 1380 | |
|
1380 | 1381 | |
|
1381 | 1382 | def initials_gravatar(request, email_address, first_name, last_name, size=30, store_on_disk=False): |
@@ -1421,7 +1422,7 b' def initials_gravatar(request, email_add' | |||
|
1421 | 1422 | file_uid=store_uid, filename=metadata["filename"], |
|
1422 | 1423 | file_hash=metadata["sha256"], file_size=metadata["size"], |
|
1423 | 1424 | file_display_name=filename, |
|
1424 |
file_description= |
|
|
1425 | file_description=f'user gravatar `{safe_str(filename)}`', | |
|
1425 | 1426 | hidden=True, check_acl=False, user_id=1 |
|
1426 | 1427 | ) |
|
1427 | 1428 | Session().add(entry) |
@@ -1694,7 +1695,7 b' def process_patterns(text_string, repo_n' | |||
|
1694 | 1695 | |
|
1695 | 1696 | log.debug('Got %s pattern entries to process', len(active_entries)) |
|
1696 | 1697 | |
|
1697 | for uid, entry in active_entries.items(): | |
|
1698 | for uid, entry in list(active_entries.items()): | |
|
1698 | 1699 | |
|
1699 | 1700 | if not (entry['pat'] and entry['url']): |
|
1700 | 1701 | log.debug('skipping due to missing data') |
@@ -1841,10 +1842,10 b" def render(source, renderer='rst', menti" | |||
|
1841 | 1842 | for issue in issues: |
|
1842 | 1843 | issues_container_callback(issue) |
|
1843 | 1844 | |
|
1844 | return literal( | |
|
1845 | '<div class="rst-block">%s</div>' % | |
|
1846 | maybe_convert_relative_links( | |
|
1847 | MarkupRenderer.rst(source, mentions=mentions))) | |
|
1845 | rendered_block = maybe_convert_relative_links( | |
|
1846 | MarkupRenderer.rst(source, mentions=mentions)) | |
|
1847 | ||
|
1848 | return literal(f'<div class="rst-block">{rendered_block}</div>') | |
|
1848 | 1849 | |
|
1849 | 1850 | elif renderer == 'markdown': |
|
1850 | 1851 | if repo_name: |
@@ -1856,18 +1857,14 b" def render(source, renderer='rst', menti" | |||
|
1856 | 1857 | for issue in issues: |
|
1857 | 1858 | issues_container_callback(issue) |
|
1858 | 1859 | |
|
1859 | ||
|
1860 | return literal( | |
|
1861 |
|
|
|
1862 | maybe_convert_relative_links( | |
|
1863 | MarkupRenderer.markdown(source, flavored=True, | |
|
1864 | mentions=mentions))) | |
|
1860 | rendered_block = maybe_convert_relative_links( | |
|
1861 | MarkupRenderer.markdown(source, flavored=True, mentions=mentions)) | |
|
1862 | return literal(f'<div class="markdown-block">{rendered_block}</div>') | |
|
1865 | 1863 | |
|
1866 | 1864 | elif renderer == 'jupyter': |
|
1867 | return literal( | |
|
1868 | '<div class="ipynb">%s</div>' % | |
|
1869 | maybe_convert_relative_links( | |
|
1870 | MarkupRenderer.jupyter(source))) | |
|
1865 | rendered_block = maybe_convert_relative_links( | |
|
1866 | MarkupRenderer.jupyter(source)) | |
|
1867 | return literal(f'<div class="ipynb">{rendered_block}</div>') | |
|
1871 | 1868 | |
|
1872 | 1869 | # None means just show the file-source |
|
1873 | 1870 | return None |
@@ -2020,10 +2017,10 b' def get_visual_attr(tmpl_context_var, at' | |||
|
2020 | 2017 | |
|
2021 | 2018 | def get_last_path_part(file_node): |
|
2022 | 2019 | if not file_node.path: |
|
2023 |
return |
|
|
2024 | ||
|
2025 |
path = safe_ |
|
|
2026 |
return |
|
|
2020 | return '/' | |
|
2021 | ||
|
2022 | path = safe_str(file_node.path.split('/')[-1]) | |
|
2023 | return '../' + path | |
|
2027 | 2024 | |
|
2028 | 2025 | |
|
2029 | 2026 | def route_url(*args, **kwargs): |
General Comments 0
You need to be logged in to leave comments.
Login now