diff --git a/ckan/lib/helpers.py b/ckan/lib/helpers.py index 4fb2228d1db..d0c1ab4234f 100644 --- a/ckan/lib/helpers.py +++ b/ckan/lib/helpers.py @@ -38,7 +38,7 @@ get_locales_dict = i18n.get_locales_dict try: - from collections import OrderedDict # from python 2.7 + from collections import OrderedDict # from python 2.7 except ImportError: from sqlalchemy.util import OrderedDict @@ -49,6 +49,7 @@ _log = logging.getLogger(__name__) + def redirect_to(*args, **kw): '''A routes.redirect_to wrapper to retain the i18n settings''' kw['__ckan_no_root'] = True @@ -56,6 +57,7 @@ def redirect_to(*args, **kw): kw['__no_cache__'] = True return _redirect_to(url_for(*args, **kw)) + def url(*args, **kw): """Create url adding i18n information if selected wrapper for pylons.url""" @@ -63,6 +65,7 @@ def url(*args, **kw): my_url = _pylons_default_url(*args, **kw) return _add_i18n_to_url(my_url, locale=locale, **kw) + def url_for(*args, **kw): """Create url adding i18n information if selected wrapper for routes.url_for""" @@ -80,6 +83,7 @@ def url_for(*args, **kw): kw['__ckan_no_root'] = no_root return _add_i18n_to_url(my_url, locale=locale, **kw) + def url_for_static(*args, **kw): """Create url for static content that does not get translated eg css, js @@ -87,6 +91,7 @@ def url_for_static(*args, **kw): my_url = _routes_default_url_for(*args, **kw) return my_url + def _add_i18n_to_url(url_to_amend, **kw): # If the locale keyword param is provided then the url is rewritten # using that locale .If return_to is provided this is used as the url @@ -149,14 +154,17 @@ def _add_i18n_to_url(url_to_amend, **kw): url = '/%s%s' % (locale, url) if url == '/packages': - raise ckan.exceptions.CkanUrlException('There is a broken url being created %s' % kw) + error = 'There is a broken url being created %s' % kw + raise ckan.exceptions.CkanUrlException(error) return url + def lang(): ''' Return the language code for the current locale eg `en` ''' return request.environ.get('CKAN_LANG') + def lang_native_name(lang=None): ''' Return the langage name currently used in it's localised form either from parameter or current environ setting''' @@ -166,6 +174,7 @@ def lang_native_name(lang=None): return locale.display_name or locale.english_name return lang + class Message(object): """A message returned by ``Flash.pop_messages()``. @@ -177,9 +186,9 @@ class Message(object): """ def __init__(self, category, message, allow_html): - self.category=category - self.message=message - self.allow_html=allow_html + self.category = category + self.message = message + self.allow_html = allow_html def __str__(self): return self.message @@ -192,6 +201,7 @@ def __html__(self): else: return escape(self.message) + class _Flash(object): # List of allowed categories. If None, allow any category. @@ -200,16 +210,19 @@ class _Flash(object): # Default category if none is specified. default_category = "" - def __init__(self, session_key="flash", categories=None, default_category=None): + def __init__(self, session_key="flash", categories=None, + default_category=None): self.session_key = session_key if categories is not None: self.categories = categories if default_category is not None: self.default_category = default_category if self.categories and self.default_category not in self.categories: - raise ValueError("unrecognized default category %r" % (self.default_category,)) + raise ValueError("unrecognized default category %r" + % (self.default_category,)) - def __call__(self, message, category=None, ignore_duplicate=False, allow_html=False): + def __call__(self, message, category=None, ignore_duplicate=False, + allow_html=False): if not category: category = self.default_category elif self.categories and category not in self.categories: @@ -244,24 +257,27 @@ def are_there_messages(self): # this is here for backwards compatability _flash = flash + def flash_notice(message, allow_html=False): ''' Show a flash message of type notice ''' flash(message, category='alert-info', allow_html=allow_html) + def flash_error(message, allow_html=False): ''' Show a flash message of type error ''' flash(message, category='alert-error', allow_html=allow_html) + def flash_success(message, allow_html=False): ''' Show a flash message of type success ''' flash(message, category='alert-success', allow_html=allow_html) + def are_there_flash_messages(): ''' Returns True if there are flash messages for the current user ''' return flash.are_there_messages() - def nav_link(*args, **kwargs): # nav_link() used to need c passing as the first arg # this is deprecated as pointless @@ -273,6 +289,7 @@ def nav_link(*args, **kwargs): raise Exception('nav_link() calling has been changed. remove c in template %s or included one' % c.__template_name) return _nav_link(*args, **kwargs) + def _nav_link(text, controller, **kwargs): highlight_actions = kwargs.pop("highlight_actions", @@ -285,6 +302,7 @@ def _nav_link(text, controller, **kwargs): else '') ) + def nav_named_link(*args, **kwargs): # subnav_link() used to need c passing as the first arg # this is deprecated as pointless @@ -297,6 +315,7 @@ def nav_named_link(*args, **kwargs): raise Exception('nav_named_link() calling has been changed. remove c in template %s or included one' % c.__template_name) return _nav_named_link(*args, **kwargs) + def _nav_named_link(text, name, **kwargs): return link_to( text, @@ -306,6 +325,7 @@ def _nav_named_link(text, name, **kwargs): # else '') ) + def subnav_link(*args, **kwargs): # subnav_link() used to need c passing as the first arg # this is deprecated as pointless @@ -317,6 +337,7 @@ def subnav_link(*args, **kwargs): raise Exception('subnav_link() calling has been changed. remove c in template %s or included one' % c.__template_name) return _subnav_link(*args, **kwargs) + def _subnav_link(text, action, **kwargs): return link_to( text, @@ -324,6 +345,7 @@ def _subnav_link(text, action, **kwargs): class_=('active' if c.action == action else '') ) + def subnav_named_route(*args, **kwargs): # subnav_link() used to need c passing as the first arg # this is deprecated as pointless @@ -336,6 +358,7 @@ def subnav_named_route(*args, **kwargs): raise Exception('subnav_named_route() calling has been changed. remove c in template %s or included one' % c.__template_name) return _subnav_named_route(*args, **kwargs) + def _subnav_named_route(text, routename, **kwargs): """ Generate a subnav element based on a named route """ return link_to( @@ -344,8 +367,10 @@ def _subnav_named_route(text, routename, **kwargs): class_=('active' if c.action == kwargs['action'] else '') ) + def default_group_type(): - return str( config.get('ckan.default.group_type', 'group') ) + return str(config.get('ckan.default.group_type', 'group')) + def unselected_facet_items(facet, limit=10): '''Return the list of unselected facet items for the given facet, sorted @@ -377,9 +402,11 @@ def unselected_facet_items(facet, limit=10): facets.append(facet_item) return sorted(facets, key=lambda item: item['count'], reverse=True)[:limit] + def facet_title(name): return config.get('search.facets.%s.title' % name, name.capitalize()) + @deprecated('Please use check_access instead.') def am_authorized(c, action, domain_object=None): ''' Deprecated. Please use check_access instead''' @@ -388,20 +415,22 @@ def am_authorized(c, action, domain_object=None): domain_object = model.System() return Authorizer.am_authorized(c, action, domain_object) + def check_access(action, data_dict=None): - from ckan.logic import check_access as check_access_logic,NotAuthorized + from ckan.logic import check_access as check_access_logic, NotAuthorized context = {'model': model, 'user': c.user or c.author} try: - check_access_logic(action,context,data_dict) + check_access_logic(action, context, data_dict) authorized = True except NotAuthorized: authorized = False return authorized + def linked_user(user, maxlength=0): if user in [model.PSEUDO_USER__LOGGED_IN, model.PSEUDO_USER__VISITOR]: return user @@ -411,13 +440,14 @@ def linked_user(user, maxlength=0): if not user: return user_name if user: - _name = user.name if model.User.VALID_NAME.match(user.name) else user.id - _icon = gravatar(user.email_hash, 20) + name = user.name if model.User.VALID_NAME.match(user.name) else user.id + icon = gravatar(user.email_hash, 20) displayname = user.display_name if maxlength and len(user.display_name) > maxlength: displayname = displayname[:maxlength] + '...' - return _icon + link_to(displayname, - url_for(controller='user', action='read', id=_name)) + return icon + link_to(displayname, + url_for(controller='user', action='read', id=name)) + def linked_authorization_group(authgroup, maxlength=0): if not isinstance(authgroup, model.AuthorizationGroup): @@ -430,7 +460,9 @@ def linked_authorization_group(authgroup, maxlength=0): if maxlength and len(display_name) > maxlength: displayname = displayname[:maxlength] + '...' return link_to(displayname, - url_for(controller='authorization_group', action='read', id=displayname)) + url_for(controller='authorization_group', + action='read', id=displayname)) + def group_name_to_title(name): group = model.Group.by_name(name) @@ -438,22 +470,30 @@ def group_name_to_title(name): return group.display_name return name + def markdown_extract(text, extract_length=190): if (text is None) or (text.strip() == ''): return '' plain = re.sub(r'<.*?>', '', markdown(text)) - return unicode(truncate(plain, length=extract_length, indicator='...', whole_word=True)) + return unicode(truncate(plain, length=extract_length, + indicator='...', whole_word=True)) + def icon_url(name): return url_for_static('/images/icons/%s.png' % name) + def icon_html(url, alt=None, inline=True): classes = '' - if inline: classes += 'inline-icon ' - return literal(' ' % (url, alt, classes)) + if inline: + classes += 'inline-icon ' + return literal((' ') % (url, alt, classes)) + def icon(name, alt=None, inline=True): - return icon_html(icon_url(name),alt,inline) + return icon_html(icon_url(name), alt, inline) + def resource_icon(res): if False: @@ -463,7 +503,8 @@ def resource_icon(res): # also: 'page_white_link' return icon(icon_name) else: - return icon(format_icon(res.get('format',''))) + return icon(format_icon(res.get('format', ''))) + def format_icon(_format): _format = _format.lower() @@ -476,14 +517,18 @@ def format_icon(_format): if ('xml' in _format): return 'page_white_code' return 'page_white' + def linked_gravatar(email_hash, size=100, default=None): return literal( '' % _('Update your avatar at gravatar.com') + - '%s' % gravatar(email_hash,size,default) + '%s' % gravatar(email_hash, size, default) ) -_VALID_GRAVATAR_DEFAULTS = ['404', 'mm', 'identicon', 'monsterid', 'wavatar', 'retro'] +_VALID_GRAVATAR_DEFAULTS = ['404', 'mm', 'identicon', 'monsterid', + 'wavatar', 'retro'] + + def gravatar(email_hash, size=100, default=None): if default is None: default = config.get('ckan.gravatar_default', 'identicon') @@ -492,10 +537,9 @@ def gravatar(email_hash, size=100, default=None): # treat the default as a url default = urllib.quote(default, safe='') - return literal('''''' - % (email_hash, size, default) - ) + return literal(('') % (email_hash, size, default)) + def pager_url(page, partial=None, **kwargs): routes_dict = _pylons_default_url.environ['pylons.routes_dict'] @@ -506,14 +550,17 @@ def pager_url(page, partial=None, **kwargs): kwargs['page'] = page return url(**kwargs) + class Page(paginate.Page): # Curry the pager method of the webhelpers.paginate.Page class, so we have # our custom layout set as default. + def pager(self, *args, **kwargs): kwargs.update( - format=u"