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 | def test_logout_form_contains_csrf(self, autologin_user, csrf_token): |
|
123 | def test_logout_form_contains_csrf(self, autologin_user, csrf_token): | |
124 | response = self.app.get(route_path('home')) |
|
124 | response = self.app.get(route_path('home')) | |
125 | assert_response = response.assert_response() |
|
125 | assert_response = response.assert_response() | |
126 |
element = assert_response.get_element('.logout |
|
126 | element = assert_response.get_element('.logout [name=csrf_token]') | |
127 | assert element.value == csrf_token |
|
127 | assert element.value == csrf_token |
@@ -21,7 +21,7 b'' | |||||
21 | import logging |
|
21 | import logging | |
22 | import urllib |
|
22 | import urllib | |
23 | from pyramid.view import view_config |
|
23 | from pyramid.view import view_config | |
24 |
from webhelpers. |
|
24 | from webhelpers2.html.tools import update_params | |
25 |
|
25 | |||
26 | from rhodecode.apps._base import BaseAppView, RepoAppView, RepoGroupAppView |
|
26 | from rhodecode.apps._base import BaseAppView, RepoAppView, RepoGroupAppView | |
27 | from rhodecode.lib.auth import ( |
|
27 | from rhodecode.lib.auth import ( |
@@ -21,13 +21,13 b'' | |||||
21 | import deform |
|
21 | import deform | |
22 | import logging |
|
22 | import logging | |
23 | import peppercorn |
|
23 | import peppercorn | |
24 | import webhelpers.paginate |
|
|||
25 |
|
24 | |||
26 | from pyramid.httpexceptions import HTTPFound, HTTPForbidden, HTTPNotFound |
|
25 | from pyramid.httpexceptions import HTTPFound, HTTPForbidden, HTTPNotFound | |
27 |
|
26 | |||
28 | from rhodecode.integrations import integration_type_registry |
|
27 | from rhodecode.integrations import integration_type_registry | |
29 | from rhodecode.apps._base import BaseAppView |
|
28 | from rhodecode.apps._base import BaseAppView | |
30 | from rhodecode.apps._base.navigation import navigation_list |
|
29 | from rhodecode.apps._base.navigation import navigation_list | |
|
30 | from rhodecode.lib.paginate import PageURL | |||
31 | from rhodecode.lib.auth import ( |
|
31 | from rhodecode.lib.auth import ( | |
32 | LoginRequired, CSRFRequired, HasPermissionAnyDecorator, |
|
32 | LoginRequired, CSRFRequired, HasPermissionAnyDecorator, | |
33 | HasRepoPermissionAnyDecorator, HasRepoGroupPermissionAnyDecorator) |
|
33 | HasRepoPermissionAnyDecorator, HasRepoGroupPermissionAnyDecorator) | |
@@ -219,8 +219,7 b' class IntegrationSettingsViewBase(BaseAp' | |||||
219 | key=lambda x: getattr(x[1], sort_field), |
|
219 | key=lambda x: getattr(x[1], sort_field), | |
220 | reverse=(sort_dir == 'desc')) |
|
220 | reverse=(sort_dir == 'desc')) | |
221 |
|
221 | |||
222 | page_url = webhelpers.paginate.PageURL( |
|
222 | page_url = PageURL(self.request.path, self.request.GET) | |
223 | self.request.path, self.request.GET) |
|
|||
224 | page = safe_int(self.request.GET.get('page', 1), 1) |
|
223 | page = safe_int(self.request.GET.get('page', 1), 1) | |
225 |
|
224 | |||
226 | integrations = h.Page( |
|
225 | integrations = h.Page( |
@@ -20,8 +20,8 b'' | |||||
20 |
|
20 | |||
21 | import logging |
|
21 | import logging | |
22 |
|
22 | |||
23 | from webhelpers.html.builder import literal |
|
23 | from webhelpers2.html.builder import literal | |
24 | from webhelpers.html.tags import link_to |
|
24 | from webhelpers2.html.tags import link_to | |
25 |
|
25 | |||
26 | from rhodecode.lib.utils2 import AttributeDict |
|
26 | from rhodecode.lib.utils2 import AttributeDict | |
27 | from rhodecode.lib.vcs.backends.base import BaseCommit |
|
27 | from rhodecode.lib.vcs.backends.base import BaseCommit |
@@ -1540,11 +1540,10 b' class CSRFRequired(object):' | |||||
1540 | http://en.wikipedia.org/wiki/Cross-site_request_forgery for more |
|
1540 | http://en.wikipedia.org/wiki/Cross-site_request_forgery for more | |
1541 | information). |
|
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', |
|
1546 | def __init__(self, token=csrf_token_key, header='X-CSRF-Token', except_methods=None): | |
1547 | except_methods=None): |
|
|||
1548 | self.token = token |
|
1547 | self.token = token | |
1549 | self.header = header |
|
1548 | self.header = header | |
1550 | self.except_methods = except_methods or [] |
|
1549 | self.except_methods = except_methods or [] |
@@ -2334,7 +2334,7 b' class RepoGroup(Base, BaseModel):' | |||||
2334 |
|
2334 | |||
2335 | @classmethod |
|
2335 | @classmethod | |
2336 | def _generate_choice(cls, repo_group): |
|
2336 | def _generate_choice(cls, repo_group): | |
2337 | from webhelpers.html import literal as _literal |
|
2337 | from webhelpers2.html import literal as _literal | |
2338 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) |
|
2338 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) | |
2339 | return repo_group.group_id, _name(repo_group.full_path_splitted) |
|
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 | @classmethod |
|
2401 | @classmethod | |
2402 | def _generate_choice(cls, repo_group): |
|
2402 | def _generate_choice(cls, repo_group): | |
2403 | from webhelpers.html import literal as _literal |
|
2403 | from webhelpers2.html import literal as _literal | |
2404 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) |
|
2404 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) | |
2405 | return repo_group.group_id, _name(repo_group.full_path_splitted) |
|
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 | @classmethod |
|
2475 | @classmethod | |
2476 | def _generate_choice(cls, repo_group): |
|
2476 | def _generate_choice(cls, repo_group): | |
2477 | from webhelpers.html import literal as _literal |
|
2477 | from webhelpers2.html import literal as _literal | |
2478 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) |
|
2478 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) | |
2479 | return repo_group.group_id, _name(repo_group.full_path_splitted) |
|
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 | @classmethod |
|
2498 | @classmethod | |
2499 | def _generate_choice(cls, repo_group): |
|
2499 | def _generate_choice(cls, repo_group): | |
2500 | from webhelpers.html import literal as _literal |
|
2500 | from webhelpers2.html import literal as _literal | |
2501 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) |
|
2501 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) | |
2502 | return repo_group.group_id, _name(repo_group.full_path_splitted) |
|
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 | @classmethod |
|
2498 | @classmethod | |
2499 | def _generate_choice(cls, repo_group): |
|
2499 | def _generate_choice(cls, repo_group): | |
2500 | from webhelpers.html import literal as _literal |
|
2500 | from webhelpers2.html import literal as _literal | |
2501 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) |
|
2501 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) | |
2502 | return repo_group.group_id, _name(repo_group.full_path_splitted) |
|
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 | from zope.cachedescriptors.property import Lazy as LazyProperty |
|
52 | from zope.cachedescriptors.property import Lazy as LazyProperty | |
53 | from pyramid import compat |
|
53 | from pyramid import compat | |
54 | from pyramid.threadlocal import get_current_request |
|
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 | from rhodecode.translation import _ |
|
57 | from rhodecode.translation import _ | |
58 | from rhodecode.lib.vcs import get_vcs_instance |
|
58 | from rhodecode.lib.vcs import get_vcs_instance | |
@@ -2624,7 +2624,7 b' class RepoGroup(Base, BaseModel):' | |||||
2624 |
|
2624 | |||
2625 | @classmethod |
|
2625 | @classmethod | |
2626 | def _generate_choice(cls, repo_group): |
|
2626 | def _generate_choice(cls, repo_group): | |
2627 | from webhelpers.html import literal as _literal |
|
2627 | from webhelpers2.html import literal as _literal | |
2628 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) |
|
2628 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) | |
2629 | return repo_group.group_id, _name(repo_group.full_path_splitted) |
|
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 | @classmethod |
|
2038 | @classmethod | |
2039 | def _generate_choice(cls, repo_group): |
|
2039 | def _generate_choice(cls, repo_group): | |
2040 | from webhelpers.html import literal as _literal |
|
2040 | from webhelpers2.html import literal as _literal | |
2041 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) |
|
2041 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) | |
2042 | return repo_group.group_id, _name(repo_group.full_path_splitted) |
|
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 | @classmethod |
|
2030 | @classmethod | |
2031 | def _generate_choice(cls, repo_group): |
|
2031 | def _generate_choice(cls, repo_group): | |
2032 | from webhelpers.html import literal as _literal |
|
2032 | from webhelpers2.html import literal as _literal | |
2033 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) |
|
2033 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) | |
2034 | return repo_group.group_id, _name(repo_group.full_path_splitted) |
|
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 | @classmethod |
|
2029 | @classmethod | |
2030 | def _generate_choice(cls, repo_group): |
|
2030 | def _generate_choice(cls, repo_group): | |
2031 | from webhelpers.html import literal as _literal |
|
2031 | from webhelpers2.html import literal as _literal | |
2032 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) |
|
2032 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) | |
2033 | return repo_group.group_id, _name(repo_group.full_path_splitted) |
|
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 | @classmethod |
|
2033 | @classmethod | |
2034 | def _generate_choice(cls, repo_group): |
|
2034 | def _generate_choice(cls, repo_group): | |
2035 | from webhelpers.html import literal as _literal |
|
2035 | from webhelpers2.html import literal as _literal | |
2036 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) |
|
2036 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) | |
2037 | return repo_group.group_id, _name(repo_group.full_path_splitted) |
|
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 | @classmethod |
|
2033 | @classmethod | |
2034 | def _generate_choice(cls, repo_group): |
|
2034 | def _generate_choice(cls, repo_group): | |
2035 | from webhelpers.html import literal as _literal |
|
2035 | from webhelpers2.html import literal as _literal | |
2036 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) |
|
2036 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) | |
2037 | return repo_group.group_id, _name(repo_group.full_path_splitted) |
|
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 | @classmethod |
|
2077 | @classmethod | |
2078 | def _generate_choice(cls, repo_group): |
|
2078 | def _generate_choice(cls, repo_group): | |
2079 | from webhelpers.html import literal as _literal |
|
2079 | from webhelpers2.html import literal as _literal | |
2080 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) |
|
2080 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) | |
2081 | return repo_group.group_id, _name(repo_group.full_path_splitted) |
|
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 | @classmethod |
|
2078 | @classmethod | |
2079 | def _generate_choice(cls, repo_group): |
|
2079 | def _generate_choice(cls, repo_group): | |
2080 | from webhelpers.html import literal as _literal |
|
2080 | from webhelpers2.html import literal as _literal | |
2081 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) |
|
2081 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) | |
2082 | return repo_group.group_id, _name(repo_group.full_path_splitted) |
|
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 | @classmethod |
|
2271 | @classmethod | |
2272 | def _generate_choice(cls, repo_group): |
|
2272 | def _generate_choice(cls, repo_group): | |
2273 | from webhelpers.html import literal as _literal |
|
2273 | from webhelpers2.html import literal as _literal | |
2274 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) |
|
2274 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) | |
2275 | return repo_group.group_id, _name(repo_group.full_path_splitted) |
|
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 | from pyramid.threadlocal import get_current_request |
|
54 | from pyramid.threadlocal import get_current_request | |
55 |
|
55 | |||
56 | from webhelpers.html import literal, HTML, escape |
|
56 | from webhelpers2.html import literal, HTML, escape | |
57 |
from webhelpers.html. |
|
57 | from webhelpers2.html._autolink import _auto_link_urls | |
58 |
from webhelpers.html. |
|
58 | from webhelpers2.html.tools import ( | |
59 | from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \ |
|
59 | button_to, highlight, js_obfuscate, strip_links, strip_tags) | |
60 | end_form, file, form as wh_form, hidden, image, javascript_link, link_to, \ |
|
60 | ||
61 | link_to_if, link_to_unless, ol, required_legend, select, stylesheet_link, \ |
|
61 | from webhelpers2.text import ( | |
62 | submit, text, password, textarea, title, ul, xml_declaration, radio |
|
62 | chop_at, collapse, convert_accented_entities, | |
63 | from webhelpers.html.tools import auto_link, button_to, highlight, \ |
|
63 | convert_misc_entities, lchop, plural, rchop, remove_formatting, | |
64 | js_obfuscate, mail_to, strip_links, strip_tags, tag_re |
|
64 | replace_whitespace, urlify, truncate, wrap_paragraphs) | |
65 | from webhelpers.text import chop_at, collapse, convert_accented_entities, \ |
|
65 | from webhelpers2.date import time_ago_in_words | |
66 | convert_misc_entities, lchop, plural, rchop, remove_formatting, \ |
|
66 | ||
67 | replace_whitespace, urlify, truncate, wrap_paragraphs |
|
67 | from webhelpers2.html.tags import ( | |
68 | from webhelpers.date import time_ago_in_words |
|
68 | _input, NotGiven, _make_safe_id_component as safeid, | |
69 | from webhelpers.paginate import Page as _Page |
|
69 | form as insecure_form, | |
70 | from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \ |
|
70 | auto_discovery_link, checkbox, end_form, file, | |
71 | convert_boolean_attrs, NotGiven, _make_safe_id_component |
|
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 | from webhelpers2.number import format_byte_size |
|
75 | from webhelpers2.number import format_byte_size | |
73 |
|
76 | |||
74 | from rhodecode.lib.action_parser import action_parser |
|
77 | from rhodecode.lib.action_parser import action_parser | |
|
78 | from rhodecode.lib.paginate import Page | |||
75 | from rhodecode.lib.ext_json import json |
|
79 | from rhodecode.lib.ext_json import json | |
76 | from rhodecode.lib.utils import repo_name_slug, get_custom_lexer |
|
80 | from rhodecode.lib.utils import repo_name_slug, get_custom_lexer | |
77 | from rhodecode.lib.utils2 import ( |
|
81 | from rhodecode.lib.utils2 import ( | |
@@ -162,17 +166,42 b' def shorter(text, size=20, prefix=False)' | |||||
162 | return text |
|
166 | return text | |
163 |
|
167 | |||
164 |
|
168 | |||
165 |
def |
|
169 | def reset(name, value=None, id=NotGiven, type="reset", **attrs): | |
166 | """ |
|
170 | """ | |
167 | Reset button |
|
171 | Reset button | |
168 | """ |
|
172 | """ | |
169 |
|
|
173 | return _input(type, name, value, id, attrs) | |
170 | _set_id_attr(attrs, id, name) |
|
174 | ||
171 | convert_boolean_attrs(attrs, ["disabled"]) |
|
175 | ||
172 | return HTML.input(**attrs) |
|
176 | def select(name, selected_values, options, id=NotGiven, **attrs): | |
173 |
|
177 | |||
174 | reset = _reset |
|
178 | if isinstance(options, (list, tuple)): | |
175 | safeid = _make_safe_id_component |
|
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 | def branding(name, length=40): |
|
207 | def branding(name, length=40): | |
@@ -1280,145 +1309,6 b' def gravatar_url(email_address, size=30,' | |||||
1280 | return initials_gravatar(email_address, '', '', size=size) |
|
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 | return formatted_size |
|
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 |
|
1459 | Extract urls from text and make html links out of them | |
1570 |
|
||||
1571 | :param text_: |
|
|||
1572 | """ |
|
1460 | """ | |
1573 |
|
1461 | |||
1574 | url_pat = re.compile(r'''(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@#.&+]''' |
|
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 | def url_func(match_obj): |
|
1465 | def url_func(match_obj): | |
1578 | url_full = match_obj.groups()[0] |
|
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 | _new_text = url_pat.sub(url_func, text_) |
|
1472 | _new_text = url_pat.sub(url_func, text_) | |
|
1473 | ||||
1581 | if safe: |
|
1474 | if safe: | |
1582 | return literal(_new_text) |
|
1475 | return literal(_new_text) | |
1583 | return _new_text |
|
1476 | return _new_text | |
@@ -1939,7 +1832,7 b" def form(url, method='post', needs_csrf_" | |||||
1939 | 'CSRF token. If the endpoint does not require such token you can ' + |
|
1832 | 'CSRF token. If the endpoint does not require such token you can ' + | |
1940 | 'explicitly set the parameter needs_csrf_token to false.') |
|
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 | def secure_form(form_url, method="POST", multipart=False, **attrs): |
|
1838 | def secure_form(form_url, method="POST", multipart=False, **attrs): | |
@@ -1961,7 +1854,6 b' def secure_form(form_url, method="POST",' | |||||
1961 | over POST. |
|
1854 | over POST. | |
1962 |
|
1855 | |||
1963 | """ |
|
1856 | """ | |
1964 | from webhelpers.pylonslib.secure_form import insecure_form |
|
|||
1965 |
|
1857 | |||
1966 | if 'request' in attrs: |
|
1858 | if 'request' in attrs: | |
1967 | session = attrs['request'].session |
|
1859 | session = attrs['request'].session | |
@@ -1970,12 +1862,12 b' def secure_form(form_url, method="POST",' | |||||
1970 | raise ValueError( |
|
1862 | raise ValueError( | |
1971 | 'Calling this form requires request= to be passed as argument') |
|
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 | token = literal( |
|
1866 | token = literal( | |
1975 |
'<input type="hidden |
|
1867 | '<input type="hidden" name="{}" value="{}">'.format( | |
1976 |
|
|
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 | def dropdownmenu(name, selected, options, enable_filter=False, **attrs): |
|
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 | import paste |
|
40 | import paste | |
41 | import pkg_resources |
|
41 | import pkg_resources | |
42 |
from webhelpers.text import collapse, remove_formatting |
|
42 | from webhelpers2.text import collapse, remove_formatting | |
43 | from mako import exceptions |
|
43 | from mako import exceptions | |
44 | from pyramid.threadlocal import get_current_registry |
|
44 | from pyramid.threadlocal import get_current_registry | |
45 | from rhodecode.lib.request import Request |
|
45 | from rhodecode.lib.request import Request |
@@ -52,7 +52,7 b' from sqlalchemy.dialects.mysql import LO' | |||||
52 | from zope.cachedescriptors.property import Lazy as LazyProperty |
|
52 | from zope.cachedescriptors.property import Lazy as LazyProperty | |
53 | from pyramid import compat |
|
53 | from pyramid import compat | |
54 | from pyramid.threadlocal import get_current_request |
|
54 | from pyramid.threadlocal import get_current_request | |
55 |
from webhelpers.text import |
|
55 | from webhelpers2.text import remove_formatting | |
56 |
|
56 | |||
57 | from rhodecode.translation import _ |
|
57 | from rhodecode.translation import _ | |
58 | from rhodecode.lib.vcs import get_vcs_instance |
|
58 | from rhodecode.lib.vcs import get_vcs_instance | |
@@ -2675,7 +2675,7 b' class RepoGroup(Base, BaseModel):' | |||||
2675 |
|
2675 | |||
2676 | @classmethod |
|
2676 | @classmethod | |
2677 | def _generate_choice(cls, repo_group): |
|
2677 | def _generate_choice(cls, repo_group): | |
2678 | from webhelpers.html import literal as _literal |
|
2678 | from webhelpers2.html import literal as _literal | |
2679 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) |
|
2679 | _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k)) | |
2680 | return repo_group.group_id, _name(repo_group.full_path_splitted) |
|
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 | @pytest.mark.parametrize('url, expected_url', [ |
|
31 | @pytest.mark.parametrize('url, expected_url', [ | |
32 |
('http://rc. |
|
32 | ('http://rc.com', '<a href="http://rc.com">http://rc.com</a>'), | |
33 |
('http://rc. |
|
33 | ('http://rc.com/test', '<a href="http://rc.com/test">http://rc.com/test</a>'), | |
34 |
('http://rc. |
|
34 | ('http://rc.com/!foo', '<a href="http://rc.com/!foo">http://rc.com/!foo</a>'), | |
35 |
('http://rc. |
|
35 | ('http://rc.com/&foo', '<a href="http://rc.com/&foo">http://rc.com/&foo</a>'), | |
36 |
('http://rc. |
|
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 | def test_urlify_text(url, expected_url): |
|
41 | def test_urlify_text(url, expected_url): | |
39 | assert helpers.urlify_text(url) == expected_url |
|
42 | assert helpers.urlify_text(url) == expected_url |
@@ -43,7 +43,7 b' import psutil' | |||||
43 | import logging |
|
43 | import logging | |
44 | import socket |
|
44 | import socket | |
45 |
|
45 | |||
46 | from webhelpers.number import format_byte_size |
|
46 | from webhelpers2.number import format_byte_size | |
47 |
|
47 | |||
48 | logging.basicConfig(level=logging.DEBUG) |
|
48 | logging.basicConfig(level=logging.DEBUG) | |
49 |
|
49 |
General Comments 0
You need to be logged in to leave comments.
Login now