-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpelican_redirect.py
179 lines (148 loc) · 6.15 KB
/
pelican_redirect.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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
from pelican import signals
from pelican.contents import Content
from pelican.readers import BaseReader
from pelican.generators import Generator
from pelican.utils import pelican_open
import email.parser
import logging
import os.path
logger = logging.getLogger(__file__)
class Redirect(Content):
mandatory_properties = ('location',)
default_template = 'redirect'
class RedirectReader(BaseReader):
"""
Reads files ending in .302 and generates an HTML file
with an appropriate meta-equiv header.
"""
enabled = True
file_extensions = ['302']
def read(self, source_path):
"""
Parse, and return (content, metadata)
"""
parser = email.parser.Parser()
with pelican_open(source_path) as source:
message = parser.parsestr(source)
location = message.get('location')
if not location:
raise ValueError(
u"RedirectReader requires a 'location' header in the file")
delay = float(message.get('delay', 0))
metadata = {
'title': message.get('title', u''),
'location': location,
'delay': delay,
'status': message.get('status', 'hidden')
}
# Slug is important because it Pelican's slugification affects
# the final URL, and we care about exact URLs here.
# So for redirect files, we assume that you named them carefully.
# And allow overriding by explicit slug.
slug = message.get('slug')
if not slug:
slug = os.path.splitext(os.path.basename(source_path))[0]
if slug:
metadata['slug'] = slug
content = message.get_payload().strip()
return content, metadata
class RedirectGenerator(Generator):
# TODO subclass CachingGenerator
def generate_context(self):
redirect_input_files = set()
input_paths = (
self.settings['PAGE_PATHS'] + self.settings['ARTICLE_PATHS']
)
# TODO there appears to be a pelican bug where get_files()
# is documented as taking a list of extensions but it actually
# only takes a string. Report upstream.
# Hacking around it.
# XXX ... or no; 'foo'.endswith(('a', 'x', 'o')) -> True
for extension in RedirectReader.file_extensions:
redirect_input_files.update(
self.get_files(paths=input_paths, extensions=extension)
)
redirects = []
# By the time this runs, the default generators have already
# run and created Page instances for all .302 files,
# and those have correct url path calculations.
# We iterate over all of those, and steal the correct paths.
# Dirty hack :)
source_path_to_final_path = {}
for type_key in ('pages', 'articles'):
content_records = self.context.get(type_key)
for cr in content_records or []:
# Get all redirects seen so far.
if cr.reader == 'redirect':
source_path_to_final_path[cr.source_path] = cr.save_as
for f in redirect_input_files:
try:
redirect = self.readers.read_file(
base_path=self.path,
path=f,
content_class=Redirect,
context=self.context,
# preread_signal=signals.article_generator_preread,
preread_sender=self,
# context_signal=signals.article_generator_context,
context_sender=self)
except Exception as e:
logger.error('Could not process redirect %s\n%s', f, e,
exc_info=self.settings.get('DEBUG', False))
self._add_failed_source_path(f)
continue
if redirect.source_path in source_path_to_final_path:
redirect.override_save_as = source_path_to_final_path[
redirect.source_path]
self.add_source_path(redirect)
redirects.append(redirect)
# print "Got a buncha redirects: %s" % redirects
self.context['redirects'] = redirects
def generate_output(self, writer):
for source in self.context['redirects']:
dest = source.save_as
template = self.get_template('redirect')
# We need to override the file because I haven't found a way
# to prevent the Page / Article generators from writing the
# .302 article as a normal (empty) Page before we get here.
# You can do that by directory (via ARTICLE_EXCLUDES) but not
# by file extension, apparently.
# So this hack prevents an error if the file is already
# marked as overridden.
#
# ... Unfortunately, dest doesn't match the path of Pages
# nested in subdirs. So this goes in the wrong place
# and the Page is still there in the wrong place.
# Where/ how do they handle subdirs?
try:
_path = os.path.join(writer.output_path, dest)
writer._overridden_files.remove(_path)
#print "Removed redirect file at %s" % _path
except KeyError:
#print "Redirect not in overridden files at %s" % _path
pass
writer.write_file(
dest, template, self.context,
page=source,
relative_urls=self.settings['RELATIVE_URLS'],
override_output=True)
def generate_feeds(self):
"""
Redirects don't go in any feeds.
"""
# print "EMPTY REDIRECT FEED for %s" % self
return
def generate_period_archives(self):
"""
Redirects don't go in any archives. (I think.)
"""
return
def add_reader(readers):
for ext in RedirectReader.file_extensions:
readers.reader_classes[ext] = RedirectReader
def get_generators(pelican_object):
# define a new generator here if you need to
return RedirectGenerator
def register():
signals.readers_init.connect(add_reader)
signals.get_generators.connect(get_generators)