Mercurial > piecrust2
comparison piecrust/appconfig.py @ 805:fd694f1297c7
config: Cleanup config loading code. Add support for a `local.yml` config.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Mon, 10 Oct 2016 21:41:59 -0700 |
parents | 58ebf50235a5 |
children | b8f089092281 |
comparison
equal
deleted
inserted
replaced
804:08e6484a2600 | 805:fd694f1297c7 |
---|---|
5 import urllib | 5 import urllib |
6 import logging | 6 import logging |
7 import hashlib | 7 import hashlib |
8 import collections | 8 import collections |
9 import yaml | 9 import yaml |
10 from piecrust import ( | 10 from piecrust import APP_VERSION, CACHE_VERSION, DEFAULT_DATE_FORMAT |
11 APP_VERSION, CACHE_VERSION, | 11 from piecrust.appconfigdefaults import ( |
12 DEFAULT_FORMAT, DEFAULT_TEMPLATE_ENGINE, DEFAULT_POSTS_FS, | 12 default_configuration, |
13 DEFAULT_DATE_FORMAT, DEFAULT_THEME_SOURCE) | 13 default_theme_content_model_base, |
14 default_content_model_base, | |
15 get_default_content_model, get_default_content_model_for_blog) | |
14 from piecrust.cache import NullCache | 16 from piecrust.cache import NullCache |
15 from piecrust.configuration import ( | 17 from piecrust.configuration import ( |
16 Configuration, ConfigurationError, ConfigurationLoader, | 18 Configuration, ConfigurationError, ConfigurationLoader, |
17 try_get_dict_value, try_get_dict_values, | 19 try_get_dict_values, try_get_dict_value, set_dict_value, |
18 set_dict_value, merge_dicts, visit_dict) | 20 merge_dicts, visit_dict, |
21 MERGE_NEW_VALUES, MERGE_OVERWRITE_VALUES, MERGE_PREPEND_LISTS, | |
22 MERGE_APPEND_LISTS) | |
19 from piecrust.sources.base import REALM_USER, REALM_THEME | 23 from piecrust.sources.base import REALM_USER, REALM_THEME |
20 | 24 |
21 | 25 |
22 logger = logging.getLogger(__name__) | 26 logger = logging.getLogger(__name__) |
27 | |
28 | |
29 class InvalidConfigurationPathError(Exception): | |
30 pass | |
23 | 31 |
24 | 32 |
25 class VariantNotFoundError(Exception): | 33 class VariantNotFoundError(Exception): |
26 def __init__(self, variant_name, message=None): | 34 def __init__(self, variant_name, message=None): |
27 super(VariantNotFoundError, self).__init__( | 35 super(VariantNotFoundError, self).__init__( |
28 message or ("No such configuration variant: %s" % | 36 message or ("No such configuration variant: %s" % |
29 variant_name)) | 37 variant_name)) |
30 | 38 |
31 | 39 |
32 class PieCrustConfiguration(Configuration): | 40 class PieCrustConfiguration(Configuration): |
33 def __init__(self, *, path=None, theme_path=None, values=None, | 41 def __init__(self, *, path=None, theme_path=None, values=None, |
34 cache=None, validate=True, theme_config=False): | 42 cache=None, validate=True, theme_config=False): |
42 self._custom_paths = [] | 50 self._custom_paths = [] |
43 self._post_fixups = [] | 51 self._post_fixups = [] |
44 self.theme_config = theme_config | 52 self.theme_config = theme_config |
45 # Set the values after we set the rest, since our validation needs | 53 # Set the values after we set the rest, since our validation needs |
46 # our attributes. | 54 # our attributes. |
47 if values: | 55 if values is not None: |
48 self.setAll(values, validate=validate) | 56 self.setAll(values, validate=validate) |
49 | 57 |
50 def addPath(self, p): | 58 def addPath(self, p): |
59 if not p: | |
60 raise InvalidConfigurationPathError() | |
51 self._ensureNotLoaded() | 61 self._ensureNotLoaded() |
52 self._custom_paths.append(p) | 62 self._custom_paths.append(p) |
53 | 63 |
54 def addVariant(self, variant_path, raise_if_not_found=True): | 64 def addVariant(self, variant_path, raise_if_not_found=True): |
55 self._ensureNotLoaded() | 65 self._ensureNotLoaded() |
56 if os.path.isfile(variant_path): | 66 if os.path.isfile(variant_path): |
57 self.addPath(variant_path) | 67 self.addPath(variant_path) |
58 elif raise_if_not_found: | 68 elif raise_if_not_found: |
59 logger.error( | 69 logger.error( |
60 "Configuration variants should now be `.yml` files " | 70 "Configuration variants should now be `.yml` files " |
61 "located in the `configs/` directory of your website.") | 71 "located in the `configs/` directory of your website.") |
62 raise VariantNotFoundError(variant_path) | 72 raise VariantNotFoundError(variant_path) |
63 | |
64 | 73 |
65 def addVariantValue(self, path, value): | 74 def addVariantValue(self, path, value): |
66 def _fixup(config): | 75 def _fixup(config): |
67 set_dict_value(config, path, value) | 76 set_dict_value(config, path, value) |
68 | 77 |
69 self._post_fixups.append(_fixup) | 78 self._post_fixups.append(_fixup) |
70 | 79 |
71 def setAll(self, values, validate=False): | 80 def setAll(self, values, validate=False): |
72 # Override base class implementation | 81 # Override base class implementation |
73 values = self._combineConfigs({}, values) | 82 values = self._processConfigs({}, values) |
74 if validate: | 83 if validate: |
75 values = self._validateAll(values) | 84 values = self._validateAll(values) |
76 self._values = values | 85 self._values = values |
77 | 86 |
78 def _ensureNotLoaded(self): | 87 def _ensureNotLoaded(self): |
79 if self._values is not None: | 88 if self._values is not None: |
80 raise Exception("The configurations has been loaded.") | 89 raise Exception("The configurations has been loaded.") |
81 | 90 |
82 def _load(self): | 91 def _load(self): |
83 # Figure out where to load this configuration from. | 92 # Figure out where to load this configuration from. |
84 paths = [self._theme_path, self._path] + self._custom_paths | 93 paths = [] |
85 paths = list(filter(lambda i: i is not None, paths)) | 94 if self._theme_path: |
95 paths.append(self._theme_path) | |
96 if self._path: | |
97 paths.append(self._path) | |
98 paths += self._custom_paths | |
99 | |
100 if len(paths) == 0: | |
101 raise ConfigurationError( | |
102 "No paths to load configuration from. " | |
103 "Specify paths, or set the values directly.") | |
86 | 104 |
87 # Build the cache-key. | 105 # Build the cache-key. |
88 path_times = [os.path.getmtime(p) for p in paths] | 106 path_times = [os.path.getmtime(p) for p in paths] |
89 cache_key_hash = hashlib.md5( | 107 cache_key_hash = hashlib.md5( |
90 ("version=%s&cache=%d" % ( | 108 ("version=%s&cache=%d" % ( |
91 APP_VERSION, CACHE_VERSION)).encode('utf8')) | 109 APP_VERSION, CACHE_VERSION)).encode('utf8')) |
92 for p in paths: | 110 for p in paths: |
93 cache_key_hash.update(("&path=%s" % p).encode('utf8')) | 111 cache_key_hash.update(("&path=%s" % p).encode('utf8')) |
94 cache_key = cache_key_hash.hexdigest() | 112 cache_key = cache_key_hash.hexdigest() |
95 | 113 |
96 # Check the cache for a valid version. | 114 # Check the cache for a valid version. |
97 if self._cache.isValid('config.json', path_times): | 115 if self._cache.isValid('config.json', path_times): |
98 logger.debug("Loading configuration from cache...") | 116 logger.debug("Loading configuration from cache...") |
99 config_text = self._cache.read('config.json') | 117 config_text = self._cache.read('config.json') |
100 self._values = json.loads( | 118 self._values = json.loads( |
101 config_text, | 119 config_text, |
102 object_pairs_hook=collections.OrderedDict) | 120 object_pairs_hook=collections.OrderedDict) |
103 | 121 |
104 actual_cache_key = self._values.get('__cache_key') | 122 actual_cache_key = self._values.get('__cache_key') |
105 if actual_cache_key == cache_key: | 123 if actual_cache_key == cache_key: |
106 # The cached version has the same key! Awesome! | 124 # The cached version has the same key! Awesome! |
107 self._values['__cache_valid'] = True | 125 self._values['__cache_valid'] = True |
108 return | 126 return |
109 logger.debug("Outdated cache key '%s' (expected '%s')." % ( | 127 logger.debug("Outdated cache key '%s' (expected '%s')." % ( |
110 actual_cache_key, cache_key)) | 128 actual_cache_key, cache_key)) |
111 | 129 |
112 # Nope, load from the paths. | 130 # Nope, load from the paths. |
113 try: | 131 try: |
114 # Theme config. | 132 # Theme values. |
115 theme_values = {} | 133 theme_values = None |
116 if self._theme_path: | 134 if self._theme_path: |
135 logger.debug("Loading theme layer from: %s" % self._theme_path) | |
117 theme_values = self._loadFrom(self._theme_path) | 136 theme_values = self._loadFrom(self._theme_path) |
118 | 137 |
119 # Site config. | 138 # Site and variant values. |
139 site_paths = [] | |
140 if self._path: | |
141 site_paths.append(self._path) | |
142 site_paths += self._custom_paths | |
143 | |
120 site_values = {} | 144 site_values = {} |
121 if self._path: | 145 for path in site_paths: |
122 site_values = self._loadFrom(self._path) | 146 logger.debug("Loading config layer from: %s" % path) |
123 | 147 cur_values = self._loadFrom(path) |
124 # Combine! | 148 merge_dicts(site_values, cur_values) |
125 logger.debug("Processing loaded configurations...") | 149 |
126 values = self._combineConfigs(theme_values, site_values) | 150 # Do it! |
127 | 151 values = self._processConfigs(theme_values, site_values) |
128 # Load additional paths. | |
129 if self._custom_paths: | |
130 logger.debug("Loading %d additional configuration paths." % | |
131 len(self._custom_paths)) | |
132 for p in self._custom_paths: | |
133 loaded = self._loadFrom(p) | |
134 if loaded: | |
135 merge_dicts(values, loaded) | |
136 | |
137 # Run final fixups | |
138 if self._post_fixups: | |
139 logger.debug("Applying %d configuration fixups." % | |
140 len(self._post_fixups)) | |
141 for f in self._post_fixups: | |
142 f(values) | |
143 | |
144 self._values = self._validateAll(values) | 152 self._values = self._validateAll(values) |
145 except Exception as ex: | 153 except Exception as ex: |
146 logger.exception(ex) | 154 logger.exception(ex) |
147 raise Exception( | 155 raise Exception( |
148 "Error loading configuration from: %s" % | 156 "Error loading configuration from: %s" % |
149 ', '.join(paths)) from ex | 157 ', '.join(paths)) from ex |
150 | 158 |
151 logger.debug("Caching configuration...") | 159 logger.debug("Caching configuration...") |
152 self._values['__cache_key'] = cache_key | 160 self._values['__cache_key'] = cache_key |
153 config_text = json.dumps(self._values) | 161 config_text = json.dumps(self._values) |
154 self._cache.write('config.json', config_text) | 162 self._cache.write('config.json', config_text) |
157 | 165 |
158 def _loadFrom(self, path): | 166 def _loadFrom(self, path): |
159 logger.debug("Loading configuration from: %s" % path) | 167 logger.debug("Loading configuration from: %s" % path) |
160 with open(path, 'r', encoding='utf-8') as fp: | 168 with open(path, 'r', encoding='utf-8') as fp: |
161 values = yaml.load( | 169 values = yaml.load( |
162 fp.read(), | 170 fp.read(), |
163 Loader=ConfigurationLoader) | 171 Loader=ConfigurationLoader) |
164 if values is None: | 172 if values is None: |
165 values = {} | 173 values = {} |
166 return values | 174 return values |
167 | 175 |
168 def _combineConfigs(self, theme_values, site_values): | 176 def _processConfigs(self, theme_values, site_values): |
169 # Start with the default configuration. | 177 # Start with the default configuration. |
170 values = copy.deepcopy(default_configuration) | 178 values = copy.deepcopy(default_configuration) |
171 | 179 |
172 if not self.theme_config: | 180 # If we have a theme, apply the theme on that. So stuff like routes |
173 # If the theme config wants the default model, add it. | 181 # will now look like: |
174 theme_sitec = theme_values.setdefault( | 182 # [custom theme] + [default theme] + [default] |
175 'site', collections.OrderedDict()) | 183 if theme_values is not None: |
176 gen_default_theme_model = bool(theme_sitec.setdefault( | 184 self._processThemeLayer(theme_values, values) |
177 'use_default_theme_content', True)) | 185 merge_dicts(values, theme_values) |
178 if gen_default_theme_model: | |
179 self._generateDefaultThemeModel(values) | |
180 | |
181 # Now override with the actual theme config values. | |
182 values = merge_dicts(values, theme_values) | |
183 | 186 |
184 # Make all sources belong to the "theme" realm at this point. | 187 # Make all sources belong to the "theme" realm at this point. |
185 srcc = values['site'].get('sources') | 188 srcc = values['site'].get('sources') |
186 if srcc: | 189 if srcc: |
187 for sn, sc in srcc.items(): | 190 for sn, sc in srcc.items(): |
188 sc['realm'] = REALM_THEME | 191 sc['realm'] = REALM_THEME |
189 | 192 |
190 # If the site config wants the default model, add it. | 193 # Now we apply the site stuff. We want to end up with: |
191 site_sitec = site_values.setdefault( | 194 # [custom site] + [default site] + [custom theme] + [default theme] + |
192 'site', collections.OrderedDict()) | 195 # [default] |
193 gen_default_site_model = bool(site_sitec.setdefault( | 196 if site_values is not None: |
194 'use_default_content', True)) | 197 self._processSiteLayer(site_values, values) |
195 if gen_default_site_model: | 198 merge_dicts(values, site_values) |
196 self._generateDefaultSiteModel(values, site_values) | |
197 | |
198 # And override with the actual site config values. | |
199 values = merge_dicts(values, site_values) | |
200 | 199 |
201 # Set the theme site flag. | 200 # Set the theme site flag. |
202 if self.theme_config: | 201 if self.theme_config: |
203 values['site']['theme_site'] = True | 202 values['site']['theme_site'] = True |
204 | 203 |
204 # Run final fixups | |
205 if self._post_fixups: | |
206 logger.debug("Applying %d configuration fixups." % | |
207 len(self._post_fixups)) | |
208 for f in self._post_fixups: | |
209 f(values) | |
210 | |
205 return values | 211 return values |
212 | |
213 def _processThemeLayer(self, theme_values, values): | |
214 # Generate the default theme model. | |
215 gen_default_theme_model = bool(try_get_dict_values( | |
216 (theme_values, 'site/use_default_theme_content'), | |
217 (values, 'site/use_default_theme_content'), | |
218 default=True)) | |
219 if gen_default_theme_model: | |
220 self._generateDefaultThemeModel(theme_values, values) | |
221 | |
222 def _processSiteLayer(self, site_values, values): | |
223 # Default site content. | |
224 gen_default_site_model = bool(try_get_dict_values( | |
225 (site_values, 'site/use_default_content'), | |
226 (values, 'site/use_default_content'), | |
227 default=True)) | |
228 if gen_default_site_model: | |
229 self._generateDefaultSiteModel(site_values, values) | |
230 | |
231 def _generateDefaultThemeModel(self, theme_values, values): | |
232 logger.debug("Generating default theme content model...") | |
233 cc = copy.deepcopy(default_theme_content_model_base) | |
234 merge_dicts(values, cc) | |
235 | |
236 def _generateDefaultSiteModel(self, site_values, values): | |
237 logger.debug("Generating default content model...") | |
238 cc = copy.deepcopy(default_content_model_base) | |
239 merge_dicts(values, cc) | |
240 | |
241 dcm = get_default_content_model(site_values, values) | |
242 merge_dicts(values, dcm) | |
243 | |
244 blogsc = try_get_dict_values( | |
245 (site_values, 'site/blogs'), | |
246 (values, 'site/blogs')) | |
247 if blogsc is None: | |
248 blogsc = ['posts'] | |
249 set_dict_value(site_values, 'site/blogs', blogsc) | |
250 | |
251 is_only_blog = (len(blogsc) == 1) | |
252 for blog_name in reversed(blogsc): | |
253 blog_cfg = get_default_content_model_for_blog( | |
254 blog_name, is_only_blog, site_values, values, | |
255 theme_site=self.theme_config) | |
256 merge_dicts(values, blog_cfg) | |
206 | 257 |
207 def _validateAll(self, values): | 258 def _validateAll(self, values): |
208 if values is None: | 259 if values is None: |
209 values = {} | 260 values = {} |
210 | 261 |
233 | 284 |
234 visit_dict(values, _visitor) | 285 visit_dict(values, _visitor) |
235 | 286 |
236 return values | 287 return values |
237 | 288 |
238 def _generateDefaultThemeModel(self, values): | |
239 logger.debug("Generating default theme content model...") | |
240 cc = copy.deepcopy(default_theme_content_model_base) | |
241 merge_dicts(values, cc) | |
242 | |
243 def _generateDefaultSiteModel(self, values, user_overrides): | |
244 logger.debug("Generating default content model...") | |
245 cc = copy.deepcopy(default_content_model_base) | |
246 merge_dicts(values, cc) | |
247 | |
248 dcm = get_default_content_model(values, user_overrides) | |
249 merge_dicts(values, dcm) | |
250 | |
251 blogsc = try_get_dict_value(user_overrides, 'site/blogs') | |
252 if blogsc is None: | |
253 blogsc = ['posts'] | |
254 set_dict_value(user_overrides, 'site/blogs', blogsc) | |
255 | |
256 is_only_blog = (len(blogsc) == 1) | |
257 for blog_name in blogsc: | |
258 blog_cfg = get_default_content_model_for_blog( | |
259 blog_name, is_only_blog, values, user_overrides, | |
260 theme_site=self.theme_config) | |
261 merge_dicts(values, blog_cfg) | |
262 | |
263 | 289 |
264 class _ConfigCacheWriter(object): | 290 class _ConfigCacheWriter(object): |
265 def __init__(self, cache_dict): | 291 def __init__(self, cache_dict): |
266 self._cache_dict = cache_dict | 292 self._cache_dict = cache_dict |
267 | 293 |
268 def write(self, name, val): | 294 def write(self, name, val): |
269 logger.debug("Caching configuration item '%s' = %s" % (name, val)) | 295 logger.debug("Caching configuration item '%s' = %s" % (name, val)) |
270 self._cache_dict[name] = val | 296 self._cache_dict[name] = val |
271 | |
272 | |
273 default_theme_content_model_base = collections.OrderedDict({ | |
274 'site': collections.OrderedDict({ | |
275 'sources': collections.OrderedDict({ | |
276 'theme_pages': { | |
277 'type': 'default', | |
278 'ignore_missing_dir': True, | |
279 'fs_endpoint': 'pages', | |
280 'data_endpoint': 'site.pages', | |
281 'default_layout': 'default', | |
282 'item_name': 'page', | |
283 'realm': REALM_THEME | |
284 } | |
285 }), | |
286 'routes': [ | |
287 { | |
288 'url': '/%slug%', | |
289 'source': 'theme_pages', | |
290 'func': 'pcurl' | |
291 } | |
292 ], | |
293 'theme_tag_page': 'theme_pages:_tag.%ext%', | |
294 'theme_category_page': 'theme_pages:_category.%ext%', | |
295 'theme_month_page': 'theme_pages:_month.%ext%', | |
296 'theme_year_page': 'theme_pages:_year.%ext%' | |
297 }) | |
298 }) | |
299 | |
300 | |
301 default_configuration = collections.OrderedDict({ | |
302 'site': collections.OrderedDict({ | |
303 'title': "Untitled PieCrust website", | |
304 'root': '/', | |
305 'default_format': DEFAULT_FORMAT, | |
306 'default_template_engine': DEFAULT_TEMPLATE_ENGINE, | |
307 'enable_gzip': True, | |
308 'pretty_urls': False, | |
309 'trailing_slash': False, | |
310 'date_format': DEFAULT_DATE_FORMAT, | |
311 'auto_formats': collections.OrderedDict([ | |
312 ('html', ''), | |
313 ('md', 'markdown'), | |
314 ('textile', 'textile')]), | |
315 'default_auto_format': 'md', | |
316 'default_pagination_source': None, | |
317 'pagination_suffix': '/%num%', | |
318 'slugify_mode': 'encode', | |
319 'themes_sources': [DEFAULT_THEME_SOURCE], | |
320 'cache_time': 28800, | |
321 'enable_debug_info': True, | |
322 'show_debug_info': False, | |
323 'use_default_content': True, | |
324 'use_default_theme_content': True, | |
325 'theme_site': False | |
326 }), | |
327 'baker': collections.OrderedDict({ | |
328 'no_bake_setting': 'draft', | |
329 'workers': None, | |
330 'batch_size': None | |
331 }) | |
332 }) | |
333 | |
334 | |
335 default_content_model_base = collections.OrderedDict({ | |
336 'site': collections.OrderedDict({ | |
337 'posts_fs': DEFAULT_POSTS_FS, | |
338 'default_page_layout': 'default', | |
339 'default_post_layout': 'post', | |
340 'post_url': '/%year%/%month%/%day%/%slug%', | |
341 'year_url': '/archives/%year%', | |
342 'tag_url': '/tag/%tag%', | |
343 'category_url': '/%category%', | |
344 'posts_per_page': 5 | |
345 }) | |
346 }) | |
347 | |
348 | |
349 def get_default_content_model(values, user_overrides): | |
350 default_layout = try_get_dict_value( | |
351 user_overrides, 'site/default_page_layout', | |
352 values['site']['default_page_layout']) | |
353 return collections.OrderedDict({ | |
354 'site': collections.OrderedDict({ | |
355 'sources': collections.OrderedDict({ | |
356 'pages': { | |
357 'type': 'default', | |
358 'ignore_missing_dir': True, | |
359 'data_endpoint': 'site.pages', | |
360 'default_layout': default_layout, | |
361 'item_name': 'page' | |
362 } | |
363 }), | |
364 'routes': [ | |
365 { | |
366 'url': '/%slug%', | |
367 'source': 'pages', | |
368 'func': 'pcurl' | |
369 } | |
370 ], | |
371 'taxonomies': collections.OrderedDict([ | |
372 ('tags', { | |
373 'multiple': True, | |
374 'term': 'tag' | |
375 }), | |
376 ('categories', { | |
377 'term': 'category', | |
378 'func_name': 'pccaturl' | |
379 }) | |
380 ]) | |
381 }) | |
382 }) | |
383 | |
384 | |
385 def get_default_content_model_for_blog( | |
386 blog_name, is_only_blog, values, user_overrides, theme_site=False): | |
387 # Get the global (default) values for various things we're interested in. | |
388 defs = {} | |
389 names = ['posts_fs', 'posts_per_page', 'date_format', | |
390 'default_post_layout', 'post_url', 'year_url'] | |
391 for n in names: | |
392 defs[n] = try_get_dict_value( | |
393 user_overrides, 'site/%s' % n, | |
394 values['site'][n]) | |
395 | |
396 # More stuff we need. | |
397 if is_only_blog: | |
398 url_prefix = '' | |
399 page_prefix = '' | |
400 fs_endpoint = 'posts' | |
401 data_endpoint = 'blog' | |
402 item_name = 'post' | |
403 tpl_func_prefix = 'pc' | |
404 | |
405 if theme_site: | |
406 # If this is a theme site, show posts from a `sample` directory | |
407 # so it's clearer that those won't show up when the theme is | |
408 # actually applied to a normal site. | |
409 fs_endpoint = 'sample/posts' | |
410 else: | |
411 url_prefix = blog_name + '/' | |
412 page_prefix = blog_name + '/' | |
413 data_endpoint = blog_name | |
414 fs_endpoint = 'posts/%s' % blog_name | |
415 item_name = try_get_dict_value(user_overrides, | |
416 '%s/item_name' % blog_name, | |
417 '%spost' % blog_name) | |
418 tpl_func_prefix = try_get_dict_value(user_overrides, | |
419 '%s/func_prefix' % blog_name, | |
420 'pc%s' % blog_name) | |
421 | |
422 # Figure out the settings values for this blog, specifically. | |
423 # The value could be set on the blog config itself, globally, or left at | |
424 # its default. We already handle the "globally vs. default" with the | |
425 # `defs` map that we computed above. | |
426 blog_cfg = user_overrides.get(blog_name, {}) | |
427 blog_values = {} | |
428 for n in names: | |
429 blog_values[n] = blog_cfg.get(n, defs[n]) | |
430 | |
431 posts_fs = blog_values['posts_fs'] | |
432 posts_per_page = blog_values['posts_per_page'] | |
433 date_format = blog_values['date_format'] | |
434 default_layout = blog_values['default_post_layout'] | |
435 post_url = '/' + url_prefix + blog_values['post_url'].lstrip('/') | |
436 year_url = '/' + url_prefix + blog_values['year_url'].lstrip('/') | |
437 | |
438 year_archive = 'pages:%s_year.%%ext%%' % page_prefix | |
439 if not theme_site: | |
440 theme_year_page = values['site'].get('theme_year_page') | |
441 if theme_year_page: | |
442 year_archive += ';' + theme_year_page | |
443 | |
444 cfg = collections.OrderedDict({ | |
445 'site': collections.OrderedDict({ | |
446 'sources': collections.OrderedDict({ | |
447 blog_name: collections.OrderedDict({ | |
448 'type': 'posts/%s' % posts_fs, | |
449 'fs_endpoint': fs_endpoint, | |
450 'data_endpoint': data_endpoint, | |
451 'item_name': item_name, | |
452 'ignore_missing_dir': True, | |
453 'data_type': 'blog', | |
454 'items_per_page': posts_per_page, | |
455 'date_format': date_format, | |
456 'default_layout': default_layout | |
457 }) | |
458 }), | |
459 'generators': collections.OrderedDict({ | |
460 ('%s_archives' % blog_name): collections.OrderedDict({ | |
461 'type': 'blog_archives', | |
462 'source': blog_name, | |
463 'page': year_archive | |
464 }) | |
465 }), | |
466 'routes': [ | |
467 { | |
468 'url': post_url, | |
469 'source': blog_name, | |
470 'func': ('%sposturl' % tpl_func_prefix) | |
471 }, | |
472 { | |
473 'url': year_url, | |
474 'generator': ('%s_archives' % blog_name), | |
475 'func': ('%syearurl' % tpl_func_prefix) | |
476 } | |
477 ] | |
478 }) | |
479 }) | |
480 | |
481 # Add a generator and a route for each taxonomy. | |
482 taxonomies_cfg = values.get('site', {}).get('taxonomies', {}).copy() | |
483 taxonomies_cfg.update( | |
484 user_overrides.get('site', {}).get('taxonomies', {})) | |
485 for tax_name, tax_cfg in taxonomies_cfg.items(): | |
486 term = tax_cfg.get('term', tax_name) | |
487 | |
488 # Generator. | |
489 page_ref = 'pages:%s_%s.%%ext%%' % (page_prefix, term) | |
490 if not theme_site: | |
491 theme_page_ref = values['site'].get('theme_%s_page' % term) | |
492 if theme_page_ref: | |
493 page_ref += ';' + theme_page_ref | |
494 tax_gen_name = '%s_%s' % (blog_name, tax_name) | |
495 tax_gen = collections.OrderedDict({ | |
496 'type': 'taxonomy', | |
497 'source': blog_name, | |
498 'taxonomy': tax_name, | |
499 'page': page_ref | |
500 }) | |
501 cfg['site']['generators'][tax_gen_name] = tax_gen | |
502 | |
503 # Route. | |
504 tax_url_cfg_name = '%s_url' % term | |
505 tax_url = try_get_dict_values( | |
506 (blog_cfg, tax_url_cfg_name), | |
507 (user_overrides, 'site/%s' % tax_url_cfg_name), | |
508 (values, 'site/%s' % tax_url_cfg_name), | |
509 default=('%s/%%%s%%' % (term, term))) | |
510 tax_url = '/' + url_prefix + tax_url.lstrip('/') | |
511 tax_func_name = try_get_dict_values( | |
512 (user_overrides, 'site/taxonomies/%s/func_name' % tax_name), | |
513 (values, 'site/taxonomies/%s/func_name' % tax_name), | |
514 default=('%s%surl' % (tpl_func_prefix, term))) | |
515 tax_route = collections.OrderedDict({ | |
516 'url': tax_url, | |
517 'generator': tax_gen_name, | |
518 'taxonomy': tax_name, | |
519 'func': tax_func_name | |
520 }) | |
521 cfg['site']['routes'].append(tax_route) | |
522 | |
523 return cfg | |
524 | 297 |
525 | 298 |
526 # Configuration value validators. | 299 # Configuration value validators. |
527 # | 300 # |
528 # Make sure we have basic site stuff. | 301 # Make sure we have basic site stuff. |