# -*- coding: utf-8 -*- # Copyright (C) 2010-2019 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 # (only), as published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # # This program is dual-licensed. If you wish to learn more about the # RhodeCode Enterprise Edition, including its added features, Support services, # and proprietary license terms, please see https://rhodecode.com/licenses/ import re from webhelpers.paginate import Page as _Page from webhelpers.paginate import PageURL from webhelpers2.html import literal, HTML class Page(_Page): """ Custom pager to match rendering style with paginator """ def _get_pos(self, cur_page, max_page, items): edge = (items / 2) + 1 if (cur_page <= edge): radius = max(items / 2, items - cur_page) elif (max_page - cur_page) < edge: radius = (items - 1) - (max_page - cur_page) else: radius = items / 2 left = max(1, (cur_page - (radius))) right = min(max_page, cur_page + (radius)) return left, cur_page, right def _range(self, regexp_match): """ Return range of linked pages (e.g. '1 2 [3] 4 5 6 7 8'). Arguments: regexp_match A "re" (regular expressions) match object containing the radius of linked pages around the current page in regexp_match.group(1) as a string This function is supposed to be called as a callable in re.sub. """ radius = int(regexp_match.group(1)) # Compute the first and last page number within the radius # e.g. '1 .. 5 6 [7] 8 9 .. 12' # -> leftmost_page = 5 # -> rightmost_page = 9 leftmost_page, _cur, rightmost_page = self._get_pos(self.page, self.last_page, (radius * 2) + 1) nav_items = [] # Create a link to the first page (unless we are on the first page # or there would be no need to insert '..' spacers) if self.page != self.first_page and self.first_page < leftmost_page: nav_items.append(self._pagerlink(self.first_page, self.first_page)) # Insert dots if there are pages between the first page # and the currently displayed page range if leftmost_page - self.first_page > 1: # Wrap in a SPAN tag if nolink_attr is set text = '..' if self.dotdot_attr: text = HTML.span(c=text, **self.dotdot_attr) nav_items.append(text) for thispage in xrange(leftmost_page, rightmost_page + 1): # Hilight the current page number and do not use a link if thispage == self.page: text = '%s' % (thispage,) # Wrap in a SPAN tag if nolink_attr is set if self.curpage_attr: text = HTML.span(c=text, **self.curpage_attr) nav_items.append(text) # Otherwise create just a link to that page else: text = '%s' % (thispage,) nav_items.append(self._pagerlink(thispage, text)) # Insert dots if there are pages between the displayed # page numbers and the end of the page range if self.last_page - rightmost_page > 1: text = '..' # Wrap in a SPAN tag if nolink_attr is set if self.dotdot_attr: text = HTML.span(c=text, **self.dotdot_attr) nav_items.append(text) # Create a link to the very last page (unless we are on the last # page or there would be no need to insert '..' spacers) if self.page != self.last_page and rightmost_page < self.last_page: nav_items.append(self._pagerlink(self.last_page, self.last_page)) ## prerender links #_page_link = url.current() #nav_items.append(literal('' % (_page_link, str(int(self.page)+1)))) #nav_items.append(literal('' % (_page_link, str(int(self.page)+1)))) return self.separator.join(nav_items) def pager(self, format='~2~', page_param='page', partial_param='partial', show_if_single_page=False, separator=' ', onclick=None, symbol_first='<<', symbol_last='>>', symbol_previous='<', symbol_next='>', link_attr={'class': 'pager_link', 'rel': 'prerender'}, curpage_attr={'class': 'pager_curpage'}, dotdot_attr={'class': 'pager_dotdot'}, **kwargs): self.curpage_attr = curpage_attr self.separator = separator self.pager_kwargs = kwargs self.page_param = page_param self.partial_param = partial_param self.onclick = onclick self.link_attr = link_attr self.dotdot_attr = dotdot_attr # Don't show navigator if there is no more than one page if self.page_count == 0 or (self.page_count == 1 and not show_if_single_page): return '' from string import Template # Replace ~...~ in token format by range of pages result = re.sub(r'~(\d+)~', self._range, format) # Interpolate '%' variables result = Template(result).safe_substitute({ 'first_page': self.first_page, 'last_page': self.last_page, 'page': self.page, 'page_count': self.page_count, 'items_per_page': self.items_per_page, 'first_item': self.first_item, 'last_item': self.last_item, 'item_count': self.item_count, 'link_first': self.page > self.first_page and \ self._pagerlink(self.first_page, symbol_first) or '', 'link_last': self.page < self.last_page and \ self._pagerlink(self.last_page, symbol_last) or '', 'link_previous': self.previous_page and \ self._pagerlink(self.previous_page, symbol_previous) \ or HTML.span(symbol_previous, class_="pg-previous disabled"), 'link_next': self.next_page and \ self._pagerlink(self.next_page, symbol_next) \ or HTML.span(symbol_next, class_="pg-next disabled") }) return literal(result)