forked from esphome/esphome-docs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
seo.py
108 lines (86 loc) · 3.76 KB
/
seo.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import re
from docutils import nodes
from docutils.parsers.rst import Directive, directives
from docutils.writers._html_base import HTMLTranslator
class SEONode(nodes.General, nodes.Element):
def __init__(self, title=None, description=None, image=None,
author=None, author_twitter=None, keywords=None):
super(SEONode, self).__init__()
self.title = title
self.description = description
self.image = image
self.author = author
self.author_twitter = author_twitter
self.keywords = keywords
def seo_visit(self: HTMLTranslator, node: SEONode):
def encode_text(text):
special_characters = {ord('&'): '&',
ord('<'): '<',
ord('"'): '"',
ord('>'): '>'}
return text.translate(special_characters)
def create_content_meta(name, content):
if content is None:
return
self.meta.append('<meta name="{}" content="{}">\n'.format(name, encode_text(content)))
def create_itemprop_meta(name, content):
if content is None:
return
self.meta.append('<meta itemprop="{}" content="{}">\n'.format(name, encode_text(content)))
def create_property_meta(name, content):
if content is None:
return
self.meta.append('<meta property="{}" content="{}"/>\n'.format(name, encode_text(content)))
# Base
create_content_meta("description", node.description)
create_content_meta("keywords", node.keywords)
# Schema.org
create_itemprop_meta("name", node.title)
create_itemprop_meta("description", node.description)
create_itemprop_meta("image", node.image)
# Twitter
create_content_meta("twitter:title", node.title)
create_content_meta("twitter:image:src", node.image)
create_content_meta("twitter:card", "summary_large_image")
create_content_meta("twitter:site", "@OttoWinter_")
create_content_meta("twitter:creator", node.author_twitter)
create_content_meta("twitter:description", node.description)
# Open Graph
create_property_meta("og:title", node.title)
create_property_meta("og:image", node.image)
create_property_meta("og:type", "article" if node.author is not None else "website")
create_property_meta("og:site_name", "esphomelib")
create_property_meta("og:description", node.description)
# Misc
create_content_meta("HandheldFriendly", "True")
create_content_meta("MobileOptimized", "320")
create_content_meta("theme-color", "#DFDFDF")
def seo_depart(self, _):
pass
class SEODirective(Directive):
option_spec = {
'title': directives.unchanged,
'description': directives.unchanged,
'image': directives.path,
'author': directives.unchanged,
'author_twitter': directives.unchanged,
'keywords': directives.unchanged,
}
def run(self):
env = self.state.document.settings.env
title_match = re.match(r'.+<title>(.+)</title>.+', str(self.state.document))
if title_match is not None and 'title' not in self.options:
self.options['title'] = title_match.group(1)
image = self.options.get('image')
if image is not None:
if not image.startswith('/'):
image = '/_images/' + image
self.options['image'] = env.config.html_baseurl + image
description = self.options.get('description')
if description is not None and len(description) >= 200:
self.options['description'] = description[:200].rsplit(' ', 1)[0] + '...'
return [SEONode(**self.options)]
def setup(app):
app.add_directive('seo', SEODirective)
app.add_node(SEONode, html=(seo_visit, seo_depart))
return {'version': '1.0'}