Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added simple "create 404 page" plugin. #175

Merged
merged 13 commits into from Oct 9, 2016
Merged
19 changes: 19 additions & 0 deletions v7/errorpages/README.md
@@ -0,0 +1,19 @@
This plugin allows to create HTTP error pages (`404.html`, `403.html`, etc.) which can be used as error pages for your web server (for 404, 403, etc. errors). These pages never use relative links, no matter whether you use relative links for the rest of your blog or not.

To use this, you need a template `XXX.tmpl` for every [HTTP status code](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes) `XXX`. The plugin provides simple templates `404.tmpl` both for Mako and Jinja, but you can easily adjust them to your needs. These default templates assume that the strings `'Page not found'` and `'The page you are trying to access does not exist. Please use your browser\'s "back" button to return to the previous page.'` are translated. If your blog uses another language than English, you need to translate these strings yourself and provide the translations in your theme's `messages_XX.py` files. Or (for simple one-language blogs) simply adjust the strings in `XXX.tmpl`.

To tell the plugin which error pages to create, add `CREATE_ERROR_PAGES` to your `conf.py`. This must be a list of error codes, like
Copy link
Member

@Kwpolska Kwpolska Oct 9, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still uses old variable name here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for noticing! Fixed it.

~~~
# Create 404 error page
CREATE_HTTP_ERROR_PAGES = [404]
~~~
for a 404 error page, and
~~~
# Create 401, 403 and 404 error pages
CREATE_HTTP_ERROR_PAGES = [401, 403, 404]
~~~
for both 403 and 404 error pages. Note that you need to provide a `403.tmpl` template for the latter example.

You also need to configure your web server accordingly. You can find documentation on how to do that for common webservers here: [Apache](https://httpd.apache.org/docs/2.4/custom-error.html), [Nginx](http://nginx.org/en/docs/http/ngx_http_core_module.html#error_page). It will work out of the box on [GitHub Pages](https://help.github.com/articles/creating-a-custom-404-page-for-your-github-pages-site/).

Note that this plugin requires version 7.8.1 of Nikola. If that version isn't released yet, you need the current GitHub `master` branch.
2 changes: 2 additions & 0 deletions v7/errorpages/conf.py.sample
@@ -0,0 +1,2 @@
# Create 404 error page
CREATE_HTTP_ERROR_PAGES = [404]
12 changes: 12 additions & 0 deletions v7/errorpages/errorpages.plugin
@@ -0,0 +1,12 @@
[Core]
Name = errorpages
Module = errorpages

[Nikola]
plugincategory = Task
MinVersion = 7.8.1

[Documentation]
Author = Felix Fontein
Version = 1.0
Description = Creates pages to be used for HTTP status/error pages.
157 changes: 157 additions & 0 deletions v7/errorpages/errorpages.py
@@ -0,0 +1,157 @@
# -*- coding: utf-8 -*-

# Copyright © 2016 Felix Fontein
#
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
# documentation files (the "Software"), to deal in the
# Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the
# Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice
# shall be included in all copies or substantial portions of
# the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

from nikola.plugin_categories import Task
from nikola import utils

import copy
import os
import os.path


class CreateErrorPages(Task):
name = "errorpages"

http_status_codes = {
100: 'Continue',
101: 'Switching Protocols',
102: 'Processing',
200: 'OK',
201: 'Created',
202: 'Accepted',
203: 'Non-Authoritative Information',
204: 'No Content',
205: 'Reset Content',
206: 'Partial Content',
207: 'Multi-Status',
208: 'Already Reported',
226: 'IM Used',
300: 'Multiple Choices',
301: 'Moved Permanently',
302: 'Found',
303: 'See Other',
304: 'Not Modified',
305: 'Use Proxy',
306: 'Switch Proxy',
307: 'Temporary Redirect',
308: 'Permanent Redirect',
400: 'Bad Request',
401: 'Unauthorized',
402: 'Payment Required',
403: 'Forbidden',
404: 'Not Found',
405: 'Method Not Allowed',
406: 'Not Acceptable',
407: 'Proxy Authentication Required',
408: 'Request Timeout',
409: 'Conflict',
410: 'Gone',
411: 'Length Required',
412: 'Precondition Failed',
413: 'Payload Too Large',
414: 'URI Too Long',
415: 'Unsupported Media Type',
416: 'Range Not Satisfiable',
417: 'Expectation Failed',
418: 'I\'m a teapot',
421: 'Misdirected Request',
422: 'Unprocessable Entity',
423: 'Locked',
424: 'Failed Dependency',
426: 'Upgrade Required',
428: 'Precondition Required',
429: 'Too Many Requests',
431: 'Request Header Fields Too Large',
451: 'Unavailable For Legal Reasons',
500: 'Internal Server Error',
501: 'Not Implemented',
502: 'Bad Gateway',
503: 'Service Unavailable',
504: 'Gateway Timeout',
505: 'HTTP Version Not Supported',
506: 'Variant Also Negotiates',
507: 'Insufficient Storage',
508: 'Loop Detected',
510: 'Not Extended',
511: 'Network Authentication Required',
}

def prepare_error_page(self, destination, lang, http_error_code, template):
http_error_message = self.http_status_codes.get(http_error_code)

title = self.site.MESSAGES[lang].get('http-error-code-{0}'.format(http_error_code))
if title is None and http_error_message is not None:
title = self.site.MESSAGES[lang].get(http_error_message, http_error_message)

context = {}
context['http_error_code'] = http_error_code
context['http_error_message'] = http_error_message
if title is not None:
context['title'] = title

deps = self.site.template_system.template_deps(template)
deps.extend(utils.get_asset_path(x, self.site.THEMES) for x in ('bundles', 'parent', 'engine'))
deps = list(filter(None, deps))
context['lang'] = lang
deps_dict = copy.copy(context)
deps_dict['OUTPUT_FOLDER'] = self.site.config['OUTPUT_FOLDER']
deps_dict['TRANSLATIONS'] = self.site.config['TRANSLATIONS']
deps_dict['global'] = self.site.GLOBAL_CONTEXT

for k, v in self.site.GLOBAL_CONTEXT['template_hooks'].items():
deps_dict['||template_hooks|{0}||'.format(k)] = v._items

for k in self.site._GLOBAL_CONTEXT_TRANSLATABLE:
deps_dict[k] = deps_dict['global'][k](lang)

deps_dict['navigation_links'] = deps_dict['global']['navigation_links'](lang)

url_type = None
if self.site.config['URL_TYPE'] == 'rel_path':
url_type = 'full_path'

task = {
'basename': self.name,
'name': os.path.normpath(destination),
'file_dep': deps,
'targets': [destination],
'actions': [(self.site.render_template, [template, destination, context, url_type])],
'clean': True,
'uptodate': [utils.config_changed(deps_dict, 'nikola.plugins.render_error_pages')]
}

yield utils.apply_filters(task, self.site.config["FILTERS"])

def gen_tasks(self):
yield self.group_task()

output_pattern = self.site.config.get('HTTP_ERROR_PAGE_OUTPUT_PATTERN', '{code}.html')
template_pattern = self.site.config.get('HTTP_ERROR_PAGE_TEMPLATE_PATTERN', '{code}.tmpl')

for error in self.site.config.get('CREATE_HTTP_ERROR_PAGES', []):
for lang in self.site.config['TRANSLATIONS'].keys():
destination = os.path.join(self.site.config['OUTPUT_FOLDER'], self.site.config['TRANSLATIONS'][lang], output_pattern.format(code=error, lang=lang))
yield self.prepare_error_page(destination, lang, error, template_pattern.format(code=error, lang=lang))
11 changes: 11 additions & 0 deletions v7/errorpages/templates/jinja/404.tmpl
@@ -0,0 +1,11 @@
{# -*- coding: utf-8 -*- #}
{% extends 'base.tmpl' %}

{% block content %}
<div class="title">
<h1 class="entry-title">{{ messages('Not Found', lang) }}</h1>
</div>
<div class="main entry-content">
{{ messages('The page you are trying to access does not exist. Please use your browser\'s "back" button to return to the previous page.', lang) }}
</div>
{% endblock %}
11 changes: 11 additions & 0 deletions v7/errorpages/templates/mako/404.tmpl
@@ -0,0 +1,11 @@
## -*- coding: utf-8 -*-
<%inherit file="base.tmpl"/>

<%block name="content">
<div class="title">
<h1 class="entry-title">{{ messages('Not Found', lang) }}</h1>
</div>
<div class="main entry-content">
{{ messages('The page you are trying to access does not exist. Please use your browser\'s "back" button to return to the previous page.', lang) }}
</div>
</%block>