webhelpers: port most of the items from webhelpers to webhelpers2...
dan -
r4090:5358a9a7 default
Not Reviewed
Show More
Add another comment
TODOs: 0 unresolved 0 Resolved
COMMENTS: 0 General 0 Inline
@@ -0,0 +1,165
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
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 #csrf_token')
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
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.util import update_params
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
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
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
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
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 ``webhelpers.secure_form`` helper functions.
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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.tools import *
57 from webhelpers2.html._autolink import _auto_link_urls
58 from webhelpers.html.builder import make_tag
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
162 return text
166 return text
163
167
164
168
165 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
169 def reset(name, value=None, id=NotGiven, type="reset", **attrs):
166 """
170 """
167 Reset button
171 Reset button
168 """
172 """
169 _set_input_attrs(attrs, type, name, value)
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
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
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