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