Source code for bottle_utils.meta

"""
.. module:: bottle_utils.meta
   :synopsis: Social metadata

.. moduleauthor:: Outernet Inc <hello@outernet.is>
"""

from __future__ import unicode_literals

from .common import attr_escape, html_escape, full_url


[docs]class MetaBase(object): """ Base class for metadata. This class is a simple placeholder to collect base functionality for various subclasses. Currently, the only functionality this base class provides is calling :py:meth:`~render` method when ``__str__()`` magic method is called on the class. """
[docs] def render(self): """ Render the meta object into HTML. In the base class this method renders to empty string. """ return ''
def __str__(self): return self.render()
[docs]class SimpleMetadata(MetaBase): """ The basic (classic) metadata. This class is used to render title tag and description meta tag. Both title and description are option. If neither is supplied, it is rendered into empty string. Here is a simple example handler:: @app.get('/my/shareable/page') @bottle.view('page') def handler(): m = SimpleMetadata(title='My page', description='A page about sharing') return dict(meta=m) In the template, simply render this object in ``<head>`` section where you would normally have the ``<title>`` tag.:: <html> <head> <meta charset="utf-8"> {{! meta }} </head> <body> ... </body> </html> This renders the following tags:: <title>My Page</title> <meta name="description" content="A page about sharing"> .. note:: In template engines that support automatic escaping of HTML, you need to suppress escaping. For instance, in SimpleTemplates, using ``{{! }}`` instead of ``{{ }}`` accomplishes this. """ def __init__(self, title='', description=''): self.title = title self.description = description @staticmethod
[docs] def meta(attr, name, value): """ Render a generic ``<meta>`` tag. This function is the basis for rendering most of the social meta tags. The arguments are rendered like this:: <meta $attr="$name" content="$value"> """ return '<meta %s="%s" content="%s">' % (attr, attr_escape(name), attr_escape(value))
[docs] def simple(self, name, value): """ Render a simple 'name' meta tag. This function renders a meta tag that uses the 'name' attribute. The arguments are rendered like this:: <meta name="$name" content="$value"> """ return self.meta('name', name, value)
[docs] def render(self): """ Render the meta object as HTML. """ s = '' if self.title: s += '<title>%s</title>' % html_escape(self.title) if self.description: s += self.simple('description', self.description) return super(SimpleMetadata, self).render() + s
[docs]class Metadata(SimpleMetadata): """ Complete set of social meta tags. This class renders a complete set of social meta tags including `schema.org <http://schema.org/>`_ properties, `OpenGraph tags <https://developers.facebook.com/docs/opengraph>`_, and `Twitter Cards markup <https://dev.twitter.com/docs/cards/markup-reference>`_. The meta tags are only rendered for the arguments that are specified (i.e., one or more of the ``title``, ``description``, ``thumbnail``, ``url``). The arguments map to common properties that have the following meanings: - title: page title - description: page description (usually used as message shown next to the post on a social network) - thumbnail: (also known as 'image') appears as a thumbnail or banner alongside the post on a social network - url: canonical URL of the page (usually an URL that does not include any query parameters that change it's appearance or generate unnecessary data, such as Google Analytics campaign tags, etc), which is used instead of the URL that was shared To render social tags, simply instantiate an object using meta data of your choosing and render the object in template (treat is as a string). Here is an example of what it may look like in a handler function:: @app.get('/my/shareable/page') @bottle.view('page') def handler(): m = Metadata(title='My page', description='A page about sharing', thumbnail='/static/images/awesome.png', url=bottle.request.path) return dict(meta=m) And here is a template:: <html> <head> <meta charset="utf-8"> {{! meta }} </head> <body> .... </body> </html> .. note:: In template engines that support automatic escaping of HTML, you need to suppress escaping. For instance, in SimpleTemplates, using ``{{! }}`` instead of ``{{ }}`` accomplishes this. This class does not render any of the other numerous tags (authorship tags, for instance). However, the instance methods it provides can be used to render them. For example, to render a Twitter creator tag in a template that has access to any instance of this class:: {{! meta.twitterprop('creator', '@OuternetForAll') }} .. note:: When it comes to thumbnails and canonical URLs, the social networks usually expect to see a full URL (including scheme and hostname). However, it may not feel right to hard-code these things. This class automatically converts paths to full URLs based on request data, so passing paths is fine. """ def __init__(self, title='', description='', thumbnail='', url=''): super(Metadata, self).__init__(title, description) if (not thumbnail) or thumbnail.startswith('http'): self.thumbnail = thumbnail self.thumbnail = thumbnail and self.make_full(thumbnail) or '' self.url = url and self.make_full(url) or '' @staticmethod
[docs] def make_full(url): """ Convert an input to full URL if not already a full URL. This static method will ensure that the specified url is a full URL. This method only checks if the provided URL starts with 'http', though, so it is possible to trick it using a path that looks like 'httpfoo': it is clearly not a full URL, but will be treated as one. If the input value is user-supplied, please perform a more through check. Under the hood, this method uses :py:func:`bottle_utils.common.full_url` to convert paths to full URLs. :param url: path or full URL :returns: full URL as per request data """ if (not url) or url.startswith('http'): return url return full_url(url)
[docs] def prop(self, namespace, name, value): """ Render a generic property meta tag. This method renders a generic property meta tags that uses ``property`` attribute to designate the tag name. Each tag name consists of two parts: namespace and name. Most notably, OpenGraph uses this form with 'og' namespace. The arguments are rendered like this:: <meta property="$namespace:$name" content="$value"> """ prop_name = '%s:%s' % (attr_escape(namespace), attr_escape(name)) return self.meta('property', prop_name, value)
[docs] def nameprop(self, namespace, name, value): """ Render a generic name property. This method renders a generic name property meta tag that uses ``name`` attribute to designate the tag name. Each tag name consist of namespace and name parts. Most notably, Twitter Card markup uses this form. The arguments are rendered like this:: <meta name="$namespace:$name" content="$value"> """ prop_name = '%s:%s' % (attr_escape(namespace), attr_escape(name)) return self.meta('name', prop_name, value)
[docs] def itemprop(self, name, value): """ Render schema.org itemprop meta tag. This method renders a schema.org itemprop meta tag which uses the ``itemprop`` attribute to designate the name of the tag. This form is used by Google, but it's otherwise an open standard for semantic markup. This method only renders meta tags, and not every other kind of markup that schema.org uses. The arguments are rendered into the following markup:: <meta itemprop="$name" content="$value"> """ return self.meta('itemprop', name, value)
[docs] def twitterprop(self, name, value): """ Renders Twitter Card markup. This method renders Twitter Card markup meta data. That is a name property meta tag with 'twitter' namespace. The arguments are rendered like so:: <meta name="twitter:$name" content="$value"> """ return self.nameprop('twitter', name, value)
[docs] def ogprop(self, name, value): """ Renders OpenGraph meta tag. This method renders a property meta tag that uses 'og' namespace. The arguments are rendered like this:: <meta property="og:$name" content="$value"> """ return self.prop('og', name, value)
[docs] def render(self): """ Render the meta object into HTML. """ s = '' if self.title: s += self.ogprop('title', self.title) s += self.twitterprop('title', self.title) s += self.itemprop('name', self.title) if self.description: s += self.ogprop('description', self.description) s += self.twitterprop('description', self.description) s += self.itemprop('description', self.description) if self.thumbnail: s += self.ogprop('image', self.thumbnail) s += self.twitterprop('image', self.thumbnail) s += self.itemprop('image', self.thumbnail) if self.url: s += self.ogprop('url', self.url) s += self.twitterprop('url', self.url) s += self.itemprop('url', self.url) return super(Metadata, self).render() + s