Show More
@@ -0,0 +1,165 b'' | |||
|
1 | # -*- coding: utf-8 -*- | |
|
2 | ||
|
3 | # Copyright (C) 2010-2019 RhodeCode GmbH | |
|
4 | # | |
|
5 | # This program is free software: you can redistribute it and/or modify | |
|
6 | # it under the terms of the GNU Affero General Public License, version 3 | |
|
7 | # (only), as published by the Free Software Foundation. | |
|
8 | # | |
|
9 | # This program is distributed in the hope that it will be useful, | |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
|
12 | # GNU General Public License for more details. | |
|
13 | # | |
|
14 | # You should have received a copy of the GNU Affero General Public License | |
|
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
|
16 | # | |
|
17 | # This program is dual-licensed. If you wish to learn more about the | |
|
18 | # RhodeCode Enterprise Edition, including its added features, Support services, | |
|
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ | |
|
20 | import re | |
|
21 | ||
|
22 | from webhelpers.paginate import Page as _Page | |
|
23 | from webhelpers.paginate import PageURL | |
|
24 | from webhelpers2.html import literal, HTML | |
|
25 | ||
|
26 | ||
|
27 | class Page(_Page): | |
|
28 | """ | |
|
29 | Custom pager to match rendering style with paginator | |
|
30 | """ | |
|
31 | ||
|
32 | def _get_pos(self, cur_page, max_page, items): | |
|
33 | edge = (items / 2) + 1 | |
|
34 | if (cur_page <= edge): | |
|
35 | radius = max(items / 2, items - cur_page) | |
|
36 | elif (max_page - cur_page) < edge: | |
|
37 | radius = (items - 1) - (max_page - cur_page) | |
|
38 | else: | |
|
39 | radius = items / 2 | |
|
40 | ||
|
41 | left = max(1, (cur_page - (radius))) | |
|
42 | right = min(max_page, cur_page + (radius)) | |
|
43 | return left, cur_page, right | |
|
44 | ||
|
45 | def _range(self, regexp_match): | |
|
46 | """ | |
|
47 | Return range of linked pages (e.g. '1 2 [3] 4 5 6 7 8'). | |
|
48 | ||
|
49 | Arguments: | |
|
50 | ||
|
51 | regexp_match | |
|
52 | A "re" (regular expressions) match object containing the | |
|
53 | radius of linked pages around the current page in | |
|
54 | regexp_match.group(1) as a string | |
|
55 | ||
|
56 | This function is supposed to be called as a callable in | |
|
57 | re.sub. | |
|
58 | ||
|
59 | """ | |
|
60 | radius = int(regexp_match.group(1)) | |
|
61 | ||
|
62 | # Compute the first and last page number within the radius | |
|
63 | # e.g. '1 .. 5 6 [7] 8 9 .. 12' | |
|
64 | # -> leftmost_page = 5 | |
|
65 | # -> rightmost_page = 9 | |
|
66 | leftmost_page, _cur, rightmost_page = self._get_pos(self.page, | |
|
67 | self.last_page, | |
|
68 | (radius * 2) + 1) | |
|
69 | nav_items = [] | |
|
70 | ||
|
71 | # Create a link to the first page (unless we are on the first page | |
|
72 | # or there would be no need to insert '..' spacers) | |
|
73 | if self.page != self.first_page and self.first_page < leftmost_page: | |
|
74 | nav_items.append(self._pagerlink(self.first_page, self.first_page)) | |
|
75 | ||
|
76 | # Insert dots if there are pages between the first page | |
|
77 | # and the currently displayed page range | |
|
78 | if leftmost_page - self.first_page > 1: | |
|
79 | # Wrap in a SPAN tag if nolink_attr is set | |
|
80 | text = '..' | |
|
81 | if self.dotdot_attr: | |
|
82 | text = HTML.span(c=text, **self.dotdot_attr) | |
|
83 | nav_items.append(text) | |
|
84 | ||
|
85 | for thispage in xrange(leftmost_page, rightmost_page + 1): | |
|
86 | # Hilight the current page number and do not use a link | |
|
87 | if thispage == self.page: | |
|
88 | text = '%s' % (thispage,) | |
|
89 | # Wrap in a SPAN tag if nolink_attr is set | |
|
90 | if self.curpage_attr: | |
|
91 | text = HTML.span(c=text, **self.curpage_attr) | |
|
92 | nav_items.append(text) | |
|
93 | # Otherwise create just a link to that page | |
|
94 | else: | |
|
95 | text = '%s' % (thispage,) | |
|
96 | nav_items.append(self._pagerlink(thispage, text)) | |
|
97 | ||
|
98 | # Insert dots if there are pages between the displayed | |
|
99 | # page numbers and the end of the page range | |
|
100 | if self.last_page - rightmost_page > 1: | |
|
101 | text = '..' | |
|
102 | # Wrap in a SPAN tag if nolink_attr is set | |
|
103 | if self.dotdot_attr: | |
|
104 | text = HTML.span(c=text, **self.dotdot_attr) | |
|
105 | nav_items.append(text) | |
|
106 | ||
|
107 | # Create a link to the very last page (unless we are on the last | |
|
108 | # page or there would be no need to insert '..' spacers) | |
|
109 | if self.page != self.last_page and rightmost_page < self.last_page: | |
|
110 | nav_items.append(self._pagerlink(self.last_page, self.last_page)) | |
|
111 | ||
|
112 | ## prerender links | |
|
113 | #_page_link = url.current() | |
|
114 | #nav_items.append(literal('<link rel="prerender" href="%s?page=%s">' % (_page_link, str(int(self.page)+1)))) | |
|
115 | #nav_items.append(literal('<link rel="prefetch" href="%s?page=%s">' % (_page_link, str(int(self.page)+1)))) | |
|
116 | return self.separator.join(nav_items) | |
|
117 | ||
|
118 | def pager(self, format='~2~', page_param='page', partial_param='partial', | |
|
119 | show_if_single_page=False, separator=' ', onclick=None, | |
|
120 | symbol_first='<<', symbol_last='>>', | |
|
121 | symbol_previous='<', symbol_next='>', | |
|
122 | link_attr={'class': 'pager_link', 'rel': 'prerender'}, | |
|
123 | curpage_attr={'class': 'pager_curpage'}, | |
|
124 | dotdot_attr={'class': 'pager_dotdot'}, **kwargs): | |
|
125 | ||
|
126 | self.curpage_attr = curpage_attr | |
|
127 | self.separator = separator | |
|
128 | self.pager_kwargs = kwargs | |
|
129 | self.page_param = page_param | |
|
130 | self.partial_param = partial_param | |
|
131 | self.onclick = onclick | |
|
132 | self.link_attr = link_attr | |
|
133 | self.dotdot_attr = dotdot_attr | |
|
134 | ||
|
135 | # Don't show navigator if there is no more than one page | |
|
136 | if self.page_count == 0 or (self.page_count == 1 and not show_if_single_page): | |
|
137 | return '' | |
|
138 | ||
|
139 | from string import Template | |
|
140 | # Replace ~...~ in token format by range of pages | |
|
141 | result = re.sub(r'~(\d+)~', self._range, format) | |
|
142 | ||
|
143 | # Interpolate '%' variables | |
|
144 | result = Template(result).safe_substitute({ | |
|
145 | 'first_page': self.first_page, | |
|
146 | 'last_page': self.last_page, | |
|
147 | 'page': self.page, | |
|
148 | 'page_count': self.page_count, | |
|
149 | 'items_per_page': self.items_per_page, | |
|
150 | 'first_item': self.first_item, | |
|
151 | 'last_item': self.last_item, | |
|
152 | 'item_count': self.item_count, | |
|
153 | 'link_first': self.page > self.first_page and \ | |
|
154 | self._pagerlink(self.first_page, symbol_first) or '', | |
|
155 | 'link_last': self.page < self.last_page and \ | |
|
156 | self._pagerlink(self.last_page, symbol_last) or '', | |
|
157 | 'link_previous': self.previous_page and \ | |
|
158 | self._pagerlink(self.previous_page, symbol_previous) \ | |
|
159 | or HTML.span(symbol_previous, class_="pg-previous disabled"), | |
|
160 | 'link_next': self.next_page and \ | |
|
161 | self._pagerlink(self.next_page, symbol_next) \ | |
|
162 | or HTML.span(symbol_next, class_="pg-next disabled") | |
|
163 | }) | |
|
164 | ||
|
165 | return literal(result) |
@@ -123,5 +123,5 b' class TestHomeController(TestController)' | |||
|
123 | 123 | def test_logout_form_contains_csrf(self, autologin_user, csrf_token): |
|
124 | 124 | response = self.app.get(route_path('home')) |
|
125 | 125 | assert_response = response.assert_response() |
|
126 |
element = assert_response.get_element('.logout |
|
|
126 | element = assert_response.get_element('.logout [name=csrf_token]') | |
|
127 | 127 | assert element.value == csrf_token |
@@ -21,7 +21,7 b'' | |||
|
21 | 21 | import logging |
|
22 | 22 | import urllib |
|
23 | 23 | from pyramid.view import view_config |
|
24 |
from webhelpers. |
|
|
24 | from webhelpers2.html.tools import update_params | |
|
25 | 25 | |
|
26 | 26 | from rhodecode.apps._base import BaseAppView, RepoAppView, RepoGroupAppView |
|
27 | 27 | from rhodecode.lib.auth import ( |
@@ -21,13 +21,13 b'' | |||
|
21 | 21 | import deform |
|
22 | 22 | import logging |
|
23 | 23 | import peppercorn |
|
24 | import webhelpers.paginate | |
|
25 | 24 | |
|
26 | 25 | from pyramid.httpexceptions import HTTPFound, HTTPForbidden, HTTPNotFound |
|
27 | 26 | |
|
28 | 27 | from rhodecode.integrations import integration_type_registry |
|
29 | 28 | from rhodecode.apps._base import BaseAppView |
|
30 | 29 | from rhodecode.apps._base.navigation import navigation_list |
|
30 | from rhodecode.lib.paginate import PageURL | |
|
31 | 31 | from rhodecode.lib.auth import ( |
|
32 | 32 | LoginRequired, CSRFRequired, HasPermissionAnyDecorator, |
|
33 | 33 | HasRepoPermissionAnyDecorator, HasRepoGroupPermissionAnyDecorator) |
@@ -219,8 +219,7 b' class IntegrationSettingsViewBase(BaseAp' | |||
|
219 | 219 | key=lambda x: getattr(x[1], sort_field), |
|
220 | 220 | reverse=(sort_dir == 'desc')) |
|
221 | 221 | |
|
222 | page_url = webhelpers.paginate.PageURL( | |
|
223 | self.request.path, self.request.GET) | |
|
222 | page_url = PageURL(self.request.path, self.request.GET) | |
|
224 | 223 | page = safe_int(self.request.GET.get('page', 1), 1) |
|
225 | 224 | |
|
226 | 225 | integrations = h.Page( |
@@ -20,8 +20,8 b'' | |||
|
20 | 20 | |
|
21 | 21 | import logging |
|
22 | 22 | |
|
23 | from webhelpers.html.builder import literal | |
|
24 | from webhelpers.html.tags import link_to | |
|
23 | from webhelpers2.html.builder import literal | |
|
24 | from webhelpers2.html.tags import link_to | |
|
25 | 25 | |
|
26 | 26 | from rhodecode.lib.utils2 import AttributeDict |
|
27 | 27 | from rhodecode.lib.vcs.backends.base import BaseCommit |
@@ -1540,11 +1540,10 b' class CSRFRequired(object):' | |||
|
1540 | 1540 | http://en.wikipedia.org/wiki/Cross-site_request_forgery for more |
|
1541 | 1541 | information). |
|
1542 | 1542 | |
|
1543 |
For use with the `` |
|
|
1543 | For use with the ``secure_form`` helper functions. | |
|
1544 | 1544 | |
|
1545 | 1545 | """ |
|
1546 | def __init__(self, token=csrf_token_key, header='X-CSRF-Token', | |
|
1547 | except_methods=None): | |
|
1546 | def __init__(self, token=csrf_token_key, header='X-CSRF-Token', except_methods=None): | |
|
1548 | 1547 | self.token = token |
|
1549 | 1548 | self.header = header |
|
1550 | 1549 | self.except_methods = except_methods or [] |
@@ -2334,7 +2334,7 b' class RepoGroup(Base, BaseModel):' | |||
|
2334 | 2334 | |
|
2335 | 2335 | @classmethod |
|
2336 | 2336 | def _generate_choice(cls, repo_group): |
|
2337 | from webhelpers.html import literal as _literal | |
|
2337 | from webhelpers2.html import literal as _literal | |
|
2338 | 2338 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) |
|
2339 | 2339 | return repo_group.group_id, _name(repo_group.full_path_splitted) |
|
2340 | 2340 |
@@ -2400,7 +2400,7 b' class RepoGroup(Base, BaseModel):' | |||
|
2400 | 2400 | |
|
2401 | 2401 | @classmethod |
|
2402 | 2402 | def _generate_choice(cls, repo_group): |
|
2403 | from webhelpers.html import literal as _literal | |
|
2403 | from webhelpers2.html import literal as _literal | |
|
2404 | 2404 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) |
|
2405 | 2405 | return repo_group.group_id, _name(repo_group.full_path_splitted) |
|
2406 | 2406 |
@@ -2474,7 +2474,7 b' class RepoGroup(Base, BaseModel):' | |||
|
2474 | 2474 | |
|
2475 | 2475 | @classmethod |
|
2476 | 2476 | def _generate_choice(cls, repo_group): |
|
2477 | from webhelpers.html import literal as _literal | |
|
2477 | from webhelpers2.html import literal as _literal | |
|
2478 | 2478 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) |
|
2479 | 2479 | return repo_group.group_id, _name(repo_group.full_path_splitted) |
|
2480 | 2480 |
@@ -2497,7 +2497,7 b' class RepoGroup(Base, BaseModel):' | |||
|
2497 | 2497 | |
|
2498 | 2498 | @classmethod |
|
2499 | 2499 | def _generate_choice(cls, repo_group): |
|
2500 | from webhelpers.html import literal as _literal | |
|
2500 | from webhelpers2.html import literal as _literal | |
|
2501 | 2501 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) |
|
2502 | 2502 | return repo_group.group_id, _name(repo_group.full_path_splitted) |
|
2503 | 2503 |
@@ -2497,7 +2497,7 b' class RepoGroup(Base, BaseModel):' | |||
|
2497 | 2497 | |
|
2498 | 2498 | @classmethod |
|
2499 | 2499 | def _generate_choice(cls, repo_group): |
|
2500 | from webhelpers.html import literal as _literal | |
|
2500 | from webhelpers2.html import literal as _literal | |
|
2501 | 2501 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) |
|
2502 | 2502 | return repo_group.group_id, _name(repo_group.full_path_splitted) |
|
2503 | 2503 |
@@ -52,7 +52,7 b' from sqlalchemy.dialects.mysql import LO' | |||
|
52 | 52 | from zope.cachedescriptors.property import Lazy as LazyProperty |
|
53 | 53 | from pyramid import compat |
|
54 | 54 | from pyramid.threadlocal import get_current_request |
|
55 | from webhelpers.text import collapse, remove_formatting | |
|
55 | from webhelpers2.text import collapse, remove_formatting | |
|
56 | 56 | |
|
57 | 57 | from rhodecode.translation import _ |
|
58 | 58 | from rhodecode.lib.vcs import get_vcs_instance |
@@ -2624,7 +2624,7 b' class RepoGroup(Base, BaseModel):' | |||
|
2624 | 2624 | |
|
2625 | 2625 | @classmethod |
|
2626 | 2626 | def _generate_choice(cls, repo_group): |
|
2627 | from webhelpers.html import literal as _literal | |
|
2627 | from webhelpers2.html import literal as _literal | |
|
2628 | 2628 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) |
|
2629 | 2629 | return repo_group.group_id, _name(repo_group.full_path_splitted) |
|
2630 | 2630 |
@@ -2037,7 +2037,7 b' class RepoGroup(Base, BaseModel):' | |||
|
2037 | 2037 | |
|
2038 | 2038 | @classmethod |
|
2039 | 2039 | def _generate_choice(cls, repo_group): |
|
2040 | from webhelpers.html import literal as _literal | |
|
2040 | from webhelpers2.html import literal as _literal | |
|
2041 | 2041 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) |
|
2042 | 2042 | return repo_group.group_id, _name(repo_group.full_path_splitted) |
|
2043 | 2043 |
@@ -2029,7 +2029,7 b' class RepoGroup(Base, BaseModel):' | |||
|
2029 | 2029 | |
|
2030 | 2030 | @classmethod |
|
2031 | 2031 | def _generate_choice(cls, repo_group): |
|
2032 | from webhelpers.html import literal as _literal | |
|
2032 | from webhelpers2.html import literal as _literal | |
|
2033 | 2033 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) |
|
2034 | 2034 | return repo_group.group_id, _name(repo_group.full_path_splitted) |
|
2035 | 2035 |
@@ -2028,7 +2028,7 b' class RepoGroup(Base, BaseModel):' | |||
|
2028 | 2028 | |
|
2029 | 2029 | @classmethod |
|
2030 | 2030 | def _generate_choice(cls, repo_group): |
|
2031 | from webhelpers.html import literal as _literal | |
|
2031 | from webhelpers2.html import literal as _literal | |
|
2032 | 2032 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) |
|
2033 | 2033 | return repo_group.group_id, _name(repo_group.full_path_splitted) |
|
2034 | 2034 |
@@ -2032,7 +2032,7 b' class RepoGroup(Base, BaseModel):' | |||
|
2032 | 2032 | |
|
2033 | 2033 | @classmethod |
|
2034 | 2034 | def _generate_choice(cls, repo_group): |
|
2035 | from webhelpers.html import literal as _literal | |
|
2035 | from webhelpers2.html import literal as _literal | |
|
2036 | 2036 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) |
|
2037 | 2037 | return repo_group.group_id, _name(repo_group.full_path_splitted) |
|
2038 | 2038 |
@@ -2032,7 +2032,7 b' class RepoGroup(Base, BaseModel):' | |||
|
2032 | 2032 | |
|
2033 | 2033 | @classmethod |
|
2034 | 2034 | def _generate_choice(cls, repo_group): |
|
2035 | from webhelpers.html import literal as _literal | |
|
2035 | from webhelpers2.html import literal as _literal | |
|
2036 | 2036 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) |
|
2037 | 2037 | return repo_group.group_id, _name(repo_group.full_path_splitted) |
|
2038 | 2038 |
@@ -2076,7 +2076,7 b' class RepoGroup(Base, BaseModel):' | |||
|
2076 | 2076 | |
|
2077 | 2077 | @classmethod |
|
2078 | 2078 | def _generate_choice(cls, repo_group): |
|
2079 | from webhelpers.html import literal as _literal | |
|
2079 | from webhelpers2.html import literal as _literal | |
|
2080 | 2080 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) |
|
2081 | 2081 | return repo_group.group_id, _name(repo_group.full_path_splitted) |
|
2082 | 2082 |
@@ -2077,7 +2077,7 b' class RepoGroup(Base, BaseModel):' | |||
|
2077 | 2077 | |
|
2078 | 2078 | @classmethod |
|
2079 | 2079 | def _generate_choice(cls, repo_group): |
|
2080 | from webhelpers.html import literal as _literal | |
|
2080 | from webhelpers2.html import literal as _literal | |
|
2081 | 2081 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) |
|
2082 | 2082 | return repo_group.group_id, _name(repo_group.full_path_splitted) |
|
2083 | 2083 |
@@ -2270,7 +2270,7 b' class RepoGroup(Base, BaseModel):' | |||
|
2270 | 2270 | |
|
2271 | 2271 | @classmethod |
|
2272 | 2272 | def _generate_choice(cls, repo_group): |
|
2273 | from webhelpers.html import literal as _literal | |
|
2273 | from webhelpers2.html import literal as _literal | |
|
2274 | 2274 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) |
|
2275 | 2275 | return repo_group.group_id, _name(repo_group.full_path_splitted) |
|
2276 | 2276 |
@@ -53,25 +53,29 b' from pygments.lexers import (' | |||
|
53 | 53 | |
|
54 | 54 | from pyramid.threadlocal import get_current_request |
|
55 | 55 | |
|
56 | from webhelpers.html import literal, HTML, escape | |
|
57 |
from webhelpers.html. |
|
|
58 |
from webhelpers.html. |
|
|
59 | from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \ | |
|
60 | end_form, file, form as wh_form, hidden, image, javascript_link, link_to, \ | |
|
61 | link_to_if, link_to_unless, ol, required_legend, select, stylesheet_link, \ | |
|
62 | submit, text, password, textarea, title, ul, xml_declaration, radio | |
|
63 | from webhelpers.html.tools import auto_link, button_to, highlight, \ | |
|
64 | js_obfuscate, mail_to, strip_links, strip_tags, tag_re | |
|
65 | from webhelpers.text import chop_at, collapse, convert_accented_entities, \ | |
|
66 | convert_misc_entities, lchop, plural, rchop, remove_formatting, \ | |
|
67 | replace_whitespace, urlify, truncate, wrap_paragraphs | |
|
68 | from webhelpers.date import time_ago_in_words | |
|
69 | from webhelpers.paginate import Page as _Page | |
|
70 | from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \ | |
|
71 | convert_boolean_attrs, NotGiven, _make_safe_id_component | |
|
56 | from webhelpers2.html import literal, HTML, escape | |
|
57 | from webhelpers2.html._autolink import _auto_link_urls | |
|
58 | from webhelpers2.html.tools import ( | |
|
59 | button_to, highlight, js_obfuscate, strip_links, strip_tags) | |
|
60 | ||
|
61 | from webhelpers2.text import ( | |
|
62 | chop_at, collapse, convert_accented_entities, | |
|
63 | convert_misc_entities, lchop, plural, rchop, remove_formatting, | |
|
64 | replace_whitespace, urlify, truncate, wrap_paragraphs) | |
|
65 | from webhelpers2.date import time_ago_in_words | |
|
66 | ||
|
67 | from webhelpers2.html.tags import ( | |
|
68 | _input, NotGiven, _make_safe_id_component as safeid, | |
|
69 | form as insecure_form, | |
|
70 | auto_discovery_link, checkbox, end_form, file, | |
|
71 | hidden, image, javascript_link, link_to, link_to_if, link_to_unless, ol, | |
|
72 | select as raw_select, stylesheet_link, submit, text, password, textarea, | |
|
73 | ul, radio, Options) | |
|
74 | ||
|
72 | 75 | from webhelpers2.number import format_byte_size |
|
73 | 76 | |
|
74 | 77 | from rhodecode.lib.action_parser import action_parser |
|
78 | from rhodecode.lib.paginate import Page | |
|
75 | 79 | from rhodecode.lib.ext_json import json |
|
76 | 80 | from rhodecode.lib.utils import repo_name_slug, get_custom_lexer |
|
77 | 81 | from rhodecode.lib.utils2 import ( |
@@ -162,17 +166,42 b' def shorter(text, size=20, prefix=False)' | |||
|
162 | 166 | return text |
|
163 | 167 | |
|
164 | 168 | |
|
165 |
def |
|
|
169 | def reset(name, value=None, id=NotGiven, type="reset", **attrs): | |
|
166 | 170 | """ |
|
167 | 171 | Reset button |
|
168 | 172 | """ |
|
169 |
|
|
|
170 | _set_id_attr(attrs, id, name) | |
|
171 | convert_boolean_attrs(attrs, ["disabled"]) | |
|
172 | return HTML.input(**attrs) | |
|
173 | return _input(type, name, value, id, attrs) | |
|
174 | ||
|
175 | ||
|
176 | def select(name, selected_values, options, id=NotGiven, **attrs): | |
|
173 | 177 | |
|
174 | reset = _reset | |
|
175 | safeid = _make_safe_id_component | |
|
178 | if isinstance(options, (list, tuple)): | |
|
179 | options_iter = options | |
|
180 | # Handle old value,label lists ... where value also can be value,label lists | |
|
181 | options = Options() | |
|
182 | for opt in options_iter: | |
|
183 | if isinstance(opt, tuple) and len(opt) == 2: | |
|
184 | value, label = opt | |
|
185 | elif isinstance(opt, basestring): | |
|
186 | value = label = opt | |
|
187 | else: | |
|
188 | raise ValueError('invalid select option type %r' % type(opt)) | |
|
189 | ||
|
190 | if isinstance(value, (list, tuple)): | |
|
191 | option_group = options.add_optgroup(label) | |
|
192 | for opt2 in value: | |
|
193 | if isinstance(opt2, tuple) and len(opt2) == 2: | |
|
194 | group_value, group_label = opt | |
|
195 | elif isinstance(opt2, basestring): | |
|
196 | group_value = group_label = opt2 | |
|
197 | else: | |
|
198 | raise ValueError('invalid select option type %r' % type(opt2)) | |
|
199 | ||
|
200 | option_group.add_option(group_label, group_value) | |
|
201 | else: | |
|
202 | options.add_option(label, value) | |
|
203 | ||
|
204 | return raw_select(name, selected_values, options, id=id, **attrs) | |
|
176 | 205 | |
|
177 | 206 | |
|
178 | 207 | def branding(name, length=40): |
@@ -1280,145 +1309,6 b' def gravatar_url(email_address, size=30,' | |||
|
1280 | 1309 | return initials_gravatar(email_address, '', '', size=size) |
|
1281 | 1310 | |
|
1282 | 1311 | |
|
1283 | class Page(_Page): | |
|
1284 | """ | |
|
1285 | Custom pager to match rendering style with paginator | |
|
1286 | """ | |
|
1287 | ||
|
1288 | def _get_pos(self, cur_page, max_page, items): | |
|
1289 | edge = (items / 2) + 1 | |
|
1290 | if (cur_page <= edge): | |
|
1291 | radius = max(items / 2, items - cur_page) | |
|
1292 | elif (max_page - cur_page) < edge: | |
|
1293 | radius = (items - 1) - (max_page - cur_page) | |
|
1294 | else: | |
|
1295 | radius = items / 2 | |
|
1296 | ||
|
1297 | left = max(1, (cur_page - (radius))) | |
|
1298 | right = min(max_page, cur_page + (radius)) | |
|
1299 | return left, cur_page, right | |
|
1300 | ||
|
1301 | def _range(self, regexp_match): | |
|
1302 | """ | |
|
1303 | Return range of linked pages (e.g. '1 2 [3] 4 5 6 7 8'). | |
|
1304 | ||
|
1305 | Arguments: | |
|
1306 | ||
|
1307 | regexp_match | |
|
1308 | A "re" (regular expressions) match object containing the | |
|
1309 | radius of linked pages around the current page in | |
|
1310 | regexp_match.group(1) as a string | |
|
1311 | ||
|
1312 | This function is supposed to be called as a callable in | |
|
1313 | re.sub. | |
|
1314 | ||
|
1315 | """ | |
|
1316 | radius = int(regexp_match.group(1)) | |
|
1317 | ||
|
1318 | # Compute the first and last page number within the radius | |
|
1319 | # e.g. '1 .. 5 6 [7] 8 9 .. 12' | |
|
1320 | # -> leftmost_page = 5 | |
|
1321 | # -> rightmost_page = 9 | |
|
1322 | leftmost_page, _cur, rightmost_page = self._get_pos(self.page, | |
|
1323 | self.last_page, | |
|
1324 | (radius * 2) + 1) | |
|
1325 | nav_items = [] | |
|
1326 | ||
|
1327 | # Create a link to the first page (unless we are on the first page | |
|
1328 | # or there would be no need to insert '..' spacers) | |
|
1329 | if self.page != self.first_page and self.first_page < leftmost_page: | |
|
1330 | nav_items.append(self._pagerlink(self.first_page, self.first_page)) | |
|
1331 | ||
|
1332 | # Insert dots if there are pages between the first page | |
|
1333 | # and the currently displayed page range | |
|
1334 | if leftmost_page - self.first_page > 1: | |
|
1335 | # Wrap in a SPAN tag if nolink_attr is set | |
|
1336 | text = '..' | |
|
1337 | if self.dotdot_attr: | |
|
1338 | text = HTML.span(c=text, **self.dotdot_attr) | |
|
1339 | nav_items.append(text) | |
|
1340 | ||
|
1341 | for thispage in xrange(leftmost_page, rightmost_page + 1): | |
|
1342 | # Hilight the current page number and do not use a link | |
|
1343 | if thispage == self.page: | |
|
1344 | text = '%s' % (thispage,) | |
|
1345 | # Wrap in a SPAN tag if nolink_attr is set | |
|
1346 | if self.curpage_attr: | |
|
1347 | text = HTML.span(c=text, **self.curpage_attr) | |
|
1348 | nav_items.append(text) | |
|
1349 | # Otherwise create just a link to that page | |
|
1350 | else: | |
|
1351 | text = '%s' % (thispage,) | |
|
1352 | nav_items.append(self._pagerlink(thispage, text)) | |
|
1353 | ||
|
1354 | # Insert dots if there are pages between the displayed | |
|
1355 | # page numbers and the end of the page range | |
|
1356 | if self.last_page - rightmost_page > 1: | |
|
1357 | text = '..' | |
|
1358 | # Wrap in a SPAN tag if nolink_attr is set | |
|
1359 | if self.dotdot_attr: | |
|
1360 | text = HTML.span(c=text, **self.dotdot_attr) | |
|
1361 | nav_items.append(text) | |
|
1362 | ||
|
1363 | # Create a link to the very last page (unless we are on the last | |
|
1364 | # page or there would be no need to insert '..' spacers) | |
|
1365 | if self.page != self.last_page and rightmost_page < self.last_page: | |
|
1366 | nav_items.append(self._pagerlink(self.last_page, self.last_page)) | |
|
1367 | ||
|
1368 | ## prerender links | |
|
1369 | #_page_link = url.current() | |
|
1370 | #nav_items.append(literal('<link rel="prerender" href="%s?page=%s">' % (_page_link, str(int(self.page)+1)))) | |
|
1371 | #nav_items.append(literal('<link rel="prefetch" href="%s?page=%s">' % (_page_link, str(int(self.page)+1)))) | |
|
1372 | return self.separator.join(nav_items) | |
|
1373 | ||
|
1374 | def pager(self, format='~2~', page_param='page', partial_param='partial', | |
|
1375 | show_if_single_page=False, separator=' ', onclick=None, | |
|
1376 | symbol_first='<<', symbol_last='>>', | |
|
1377 | symbol_previous='<', symbol_next='>', | |
|
1378 | link_attr={'class': 'pager_link', 'rel': 'prerender'}, | |
|
1379 | curpage_attr={'class': 'pager_curpage'}, | |
|
1380 | dotdot_attr={'class': 'pager_dotdot'}, **kwargs): | |
|
1381 | ||
|
1382 | self.curpage_attr = curpage_attr | |
|
1383 | self.separator = separator | |
|
1384 | self.pager_kwargs = kwargs | |
|
1385 | self.page_param = page_param | |
|
1386 | self.partial_param = partial_param | |
|
1387 | self.onclick = onclick | |
|
1388 | self.link_attr = link_attr | |
|
1389 | self.dotdot_attr = dotdot_attr | |
|
1390 | ||
|
1391 | # Don't show navigator if there is no more than one page | |
|
1392 | if self.page_count == 0 or (self.page_count == 1 and not show_if_single_page): | |
|
1393 | return '' | |
|
1394 | ||
|
1395 | from string import Template | |
|
1396 | # Replace ~...~ in token format by range of pages | |
|
1397 | result = re.sub(r'~(\d+)~', self._range, format) | |
|
1398 | ||
|
1399 | # Interpolate '%' variables | |
|
1400 | result = Template(result).safe_substitute({ | |
|
1401 | 'first_page': self.first_page, | |
|
1402 | 'last_page': self.last_page, | |
|
1403 | 'page': self.page, | |
|
1404 | 'page_count': self.page_count, | |
|
1405 | 'items_per_page': self.items_per_page, | |
|
1406 | 'first_item': self.first_item, | |
|
1407 | 'last_item': self.last_item, | |
|
1408 | 'item_count': self.item_count, | |
|
1409 | 'link_first': self.page > self.first_page and \ | |
|
1410 | self._pagerlink(self.first_page, symbol_first) or '', | |
|
1411 | 'link_last': self.page < self.last_page and \ | |
|
1412 | self._pagerlink(self.last_page, symbol_last) or '', | |
|
1413 | 'link_previous': self.previous_page and \ | |
|
1414 | self._pagerlink(self.previous_page, symbol_previous) \ | |
|
1415 | or HTML.span(symbol_previous, class_="pg-previous disabled"), | |
|
1416 | 'link_next': self.next_page and \ | |
|
1417 | self._pagerlink(self.next_page, symbol_next) \ | |
|
1418 | or HTML.span(symbol_next, class_="pg-next disabled") | |
|
1419 | }) | |
|
1420 | ||
|
1421 | return literal(result) | |
|
1422 | 1312 | |
|
1423 | 1313 | |
|
1424 | 1314 | #============================================================================== |
@@ -1564,11 +1454,9 b' def format_byte_size_binary(file_size):' | |||
|
1564 | 1454 | return formatted_size |
|
1565 | 1455 | |
|
1566 | 1456 | |
|
1567 | def urlify_text(text_, safe=True): | |
|
1457 | def urlify_text(text_, safe=True, **href_attrs): | |
|
1568 | 1458 | """ |
|
1569 | Extrac urls from text and make html links out of them | |
|
1570 | ||
|
1571 | :param text_: | |
|
1459 | Extract urls from text and make html links out of them | |
|
1572 | 1460 | """ |
|
1573 | 1461 | |
|
1574 | 1462 | url_pat = re.compile(r'''(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@#.&+]''' |
@@ -1576,8 +1464,13 b' def urlify_text(text_, safe=True):' | |||
|
1576 | 1464 | |
|
1577 | 1465 | def url_func(match_obj): |
|
1578 | 1466 | url_full = match_obj.groups()[0] |
|
1579 | return '<a href="%(url)s">%(url)s</a>' % ({'url': url_full}) | |
|
1467 | a_options = dict(href_attrs) | |
|
1468 | a_options['href'] = url_full | |
|
1469 | a_text = url_full | |
|
1470 | return HTML.tag("a", a_text, **a_options) | |
|
1471 | ||
|
1580 | 1472 | _new_text = url_pat.sub(url_func, text_) |
|
1473 | ||
|
1581 | 1474 | if safe: |
|
1582 | 1475 | return literal(_new_text) |
|
1583 | 1476 | return _new_text |
@@ -1939,7 +1832,7 b" def form(url, method='post', needs_csrf_" | |||
|
1939 | 1832 | 'CSRF token. If the endpoint does not require such token you can ' + |
|
1940 | 1833 | 'explicitly set the parameter needs_csrf_token to false.') |
|
1941 | 1834 | |
|
1942 |
return |
|
|
1835 | return insecure_form(url, method=method, **attrs) | |
|
1943 | 1836 | |
|
1944 | 1837 | |
|
1945 | 1838 | def secure_form(form_url, method="POST", multipart=False, **attrs): |
@@ -1961,7 +1854,6 b' def secure_form(form_url, method="POST",' | |||
|
1961 | 1854 | over POST. |
|
1962 | 1855 | |
|
1963 | 1856 | """ |
|
1964 | from webhelpers.pylonslib.secure_form import insecure_form | |
|
1965 | 1857 | |
|
1966 | 1858 | if 'request' in attrs: |
|
1967 | 1859 | session = attrs['request'].session |
@@ -1970,12 +1862,12 b' def secure_form(form_url, method="POST",' | |||
|
1970 | 1862 | raise ValueError( |
|
1971 | 1863 | 'Calling this form requires request= to be passed as argument') |
|
1972 | 1864 | |
|
1973 | form = insecure_form(form_url, method, multipart, **attrs) | |
|
1865 | _form = insecure_form(form_url, method, multipart, **attrs) | |
|
1974 | 1866 | token = literal( |
|
1975 |
'<input type="hidden |
|
|
1976 |
|
|
|
1867 | '<input type="hidden" name="{}" value="{}">'.format( | |
|
1868 | csrf_token_key, get_csrf_token(session))) | |
|
1977 | 1869 | |
|
1978 | return literal("%s\n%s" % (form, token)) | |
|
1870 | return literal("%s\n%s" % (_form, token)) | |
|
1979 | 1871 | |
|
1980 | 1872 | |
|
1981 | 1873 | def dropdownmenu(name, selected, options, enable_filter=False, **attrs): |
@@ -39,7 +39,7 b' from os.path import join as jn' | |||
|
39 | 39 | |
|
40 | 40 | import paste |
|
41 | 41 | import pkg_resources |
|
42 |
from webhelpers.text import collapse, remove_formatting |
|
|
42 | from webhelpers2.text import collapse, remove_formatting | |
|
43 | 43 | from mako import exceptions |
|
44 | 44 | from pyramid.threadlocal import get_current_registry |
|
45 | 45 | from rhodecode.lib.request import Request |
@@ -52,7 +52,7 b' from sqlalchemy.dialects.mysql import LO' | |||
|
52 | 52 | from zope.cachedescriptors.property import Lazy as LazyProperty |
|
53 | 53 | from pyramid import compat |
|
54 | 54 | from pyramid.threadlocal import get_current_request |
|
55 |
from webhelpers.text import |
|
|
55 | from webhelpers2.text import remove_formatting | |
|
56 | 56 | |
|
57 | 57 | from rhodecode.translation import _ |
|
58 | 58 | from rhodecode.lib.vcs import get_vcs_instance |
@@ -2675,7 +2675,7 b' class RepoGroup(Base, BaseModel):' | |||
|
2675 | 2675 | |
|
2676 | 2676 | @classmethod |
|
2677 | 2677 | def _generate_choice(cls, repo_group): |
|
2678 | from webhelpers.html import literal as _literal | |
|
2678 | from webhelpers2.html import literal as _literal | |
|
2679 | 2679 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) |
|
2680 | 2680 | return repo_group.group_id, _name(repo_group.full_path_splitted) |
|
2681 | 2681 |
@@ -29,11 +29,14 b' from rhodecode.tests import no_newline_i' | |||
|
29 | 29 | |
|
30 | 30 | |
|
31 | 31 | @pytest.mark.parametrize('url, expected_url', [ |
|
32 |
('http://rc. |
|
|
33 |
('http://rc. |
|
|
34 |
('http://rc. |
|
|
35 |
('http://rc. |
|
|
36 |
('http://rc. |
|
|
32 | ('http://rc.com', '<a href="http://rc.com">http://rc.com</a>'), | |
|
33 | ('http://rc.com/test', '<a href="http://rc.com/test">http://rc.com/test</a>'), | |
|
34 | ('http://rc.com/!foo', '<a href="http://rc.com/!foo">http://rc.com/!foo</a>'), | |
|
35 | ('http://rc.com/&foo', '<a href="http://rc.com/&foo">http://rc.com/&foo</a>'), | |
|
36 | ('http://rc.com/?foo-1&bar=1', '<a href="http://rc.com/?foo-1&bar=1">http://rc.com/?foo-1&bar=1</a>'), | |
|
37 | ('http://rc.com?foo-1&bar=1', '<a href="http://rc.com?foo-1&bar=1">http://rc.com?foo-1&bar=1</a>'), | |
|
38 | ('http://rc.com/#foo', '<a href="http://rc.com/#foo">http://rc.com/#foo</a>'), | |
|
39 | ('http://rc.com/@foo', '<a href="http://rc.com/@foo">http://rc.com/@foo</a>'), | |
|
37 | 40 | ]) |
|
38 | 41 | def test_urlify_text(url, expected_url): |
|
39 | 42 | assert helpers.urlify_text(url) == expected_url |
General Comments 0
You need to be logged in to leave comments.
Login now