Merge remote-tracking branch 'refs/remotes/origin/master' into feature/install-nix
|
@ -3,10 +3,17 @@ python: 2.7
|
|||
install:
|
||||
- "pip install -U pip"
|
||||
- "pip install git+https://github.com/lektor/lektor#egg=Lektor"
|
||||
script: "lektor build && lektor deploy production"
|
||||
script:
|
||||
- "lektor build"
|
||||
- "test \"$TRAVIS_PULL_REQUEST\" == \"false\" && test \"$TRAVIS_BRANCH\" == \"master\" && lektor deploy production"
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.cache/pip
|
||||
- $HOME/.cache/lektor/builds
|
||||
addons:
|
||||
ssh_known_hosts: flow.srv.pocoo.org
|
||||
|
||||
notifications:
|
||||
webhooks:
|
||||
urls:
|
||||
- https://webhooks.gitter.im/e/f0c538cdfc9883f81e34
|
||||
|
|
|
@ -12,3 +12,7 @@ default = yes
|
|||
[packages]
|
||||
lektor-webpack-support = 0.1
|
||||
lektor-disqus-comments = 0.1
|
||||
lektor-markdown-header-anchors = 0.1
|
||||
lektor-markdown-highlighter = 0.1
|
||||
lektor-markdown-admonition = 0.1
|
||||
lektor-atom = 0.1
|
||||
|
|
|
@ -10,7 +10,7 @@ from _winreg import OpenKey, CloseKey, QueryValueEx, SetValueEx, \
|
|||
HKEY_CURRENT_USER, KEY_ALL_ACCESS, REG_EXPAND_SZ
|
||||
import ctypes
|
||||
from ctypes.wintypes import HWND, UINT, WPARAM, LPARAM, LPVOID
|
||||
|
||||
import shutil
|
||||
|
||||
VENV_URL = 'https://pypi.python.org/pypi/virtualenv/json'
|
||||
APPDATA = os.environ['LocalAppData']
|
||||
|
@ -22,12 +22,23 @@ LRESULT = LPARAM
|
|||
HWND_BROADCAST = 0xFFFF
|
||||
WM_SETTINGCHANGE = 0x1A
|
||||
|
||||
def find_location():
|
||||
|
||||
def get_location():
|
||||
install_dir = os.path.join(APPDATA, APP)
|
||||
return install_dir
|
||||
|
||||
def find_location():
|
||||
install_dir = get_location()
|
||||
if os.path.exists(install_dir) and os.path.isdir(install_dir):
|
||||
return None, None
|
||||
return install_dir, os.path.join(install_dir, LIB)
|
||||
|
||||
def deletion_error(func, path, excinfo):
|
||||
print 'Problem deleting {}'.format(path)
|
||||
print 'Please try and delete {} manually'.format(path)
|
||||
print 'Aborted!'
|
||||
sys.exit()
|
||||
|
||||
def fail(message):
|
||||
print 'Error: %s' % message
|
||||
sys.exit(1)
|
||||
|
@ -86,7 +97,21 @@ def main():
|
|||
|
||||
install_dir, lib_dir = find_location()
|
||||
if install_dir == None:
|
||||
fail('Lektor seems to be installed already.')
|
||||
print ' Lektor seems to be already installed at:'
|
||||
print ' {}'.format(get_location())
|
||||
while 1:
|
||||
input = raw_input(
|
||||
'Delete existing and reinstall? [Yn]'
|
||||
).lower().strip()
|
||||
|
||||
if input in ('', 'y'):
|
||||
shutil.rmtree(get_location(), onerror=deletion_error)
|
||||
break
|
||||
elif input == 'n':
|
||||
print 'Aborted!'
|
||||
sys.exit()
|
||||
|
||||
install_dir, lib_dir = find_location()
|
||||
|
||||
print ' Installing at: %s' % install_dir
|
||||
while 1:
|
||||
|
|
|
@ -11,12 +11,16 @@
|
|||
I() {
|
||||
set -u
|
||||
|
||||
if ! hash python 2> /dev/null; then
|
||||
if hash python2 2> /dev/null; then
|
||||
PY=python2
|
||||
elif hash python 2> /dev/null; then
|
||||
PY=python
|
||||
else
|
||||
echo "Error: To use this script you need to have Python installed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
python - <<'EOF'
|
||||
$PY - <<'EOF'
|
||||
if 1:
|
||||
|
||||
import os
|
||||
|
@ -99,7 +103,7 @@ if 1:
|
|||
os.makedirs(lib_dir)
|
||||
except OSError:
|
||||
pass
|
||||
Popen(['./virtualenv.py', lib_dir], cwd=t).wait()
|
||||
Popen([sys.executable, './virtualenv.py', lib_dir], cwd=t).wait()
|
||||
Popen([os.path.join(lib_dir, 'bin', 'pip'),
|
||||
'install', '--upgrade', 'Lektor']).wait()
|
||||
os.symlink(os.path.join(lib_dir, 'bin', 'lektor'),
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"version":3,"file":"app.js","sources":["webpack:///app.js","webpack:///"],"mappings":"AAAA;;;;;;;;;;;;;AC+LA;;;;;;;;;;AAqiBA;AAqpHA;AAkgIA;AAg3EA","sourceRoot":""}
|
||||
{"version":3,"file":"app.js","sources":["webpack:///app.js","webpack:///"],"mappings":"AAAA;;;;;;;;;;;;;ACyMA;;;;;;;;;;AAqiBA;AAqpHA;AAkgIA;AAg3EA","sourceRoot":""}
|
|
@ -0,0 +1,3 @@
|
|||
[transcript]
|
||||
name = The Transcript
|
||||
source_path = /blog
|
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 1.4 MiB |
|
@ -1 +1,3 @@
|
|||
_model: blog
|
||||
---
|
||||
summary: Lektor's blog
|
||||
|
|
Before Width: | Height: | Size: 226 KiB After Width: | Height: | Size: 212 KiB |
Before Width: | Height: | Size: 279 KiB After Width: | Height: | Size: 171 KiB |
Before Width: | Height: | Size: 415 KiB After Width: | Height: | Size: 368 KiB |
|
@ -100,7 +100,7 @@ also find a screencast there.
|
|||
On a very basic level Lektor takes `.lr` files which are basic text files
|
||||
with a super simple format and generates out HTML files. The `.lr` files
|
||||
correspond to a previously set up data model. They are pure text format for
|
||||
key/value pairs. Each pair is seaprated by three dashes (`---`):
|
||||
key/value pairs. Each pair is separated by three dashes (`---`):
|
||||
|
||||
```
|
||||
field_1: value
|
||||
|
|
Before Width: | Height: | Size: 241 KiB After Width: | Height: | Size: 158 KiB |
|
@ -0,0 +1,37 @@
|
|||
title: Lektor Loves Travis-CI and GitHub Pages
|
||||
---
|
||||
summary:
|
||||
|
||||
Read about how to use Lektor with Travis-CI and GitHub Pages.
|
||||
---
|
||||
pub_date: 2015-12-23
|
||||
---
|
||||
author: Armin Ronacher
|
||||
---
|
||||
twitter_handle: mitsuhiko
|
||||
---
|
||||
body:
|
||||
|
||||
#### banner ####
|
||||
image: header.jpg
|
||||
----
|
||||
height: 500
|
||||
#### text-block ####
|
||||
text:
|
||||
|
||||
Open Source projects need websites, that's a given, and one of the most popular
|
||||
ways to host them these days is [GitHub Pages :ext](https://pages.github.com/).
|
||||
It's a free service provided by [GitHub :ext](https://github.com/) which allows
|
||||
hosts a git repository as a website on a subdomain of `github.io`.
|
||||
|
||||
Wouldn't it be nice if you could easily host Lektor projects on there? Turns
|
||||
out you can with the help of [Travis-CI :ext](https://travis-ci.org/). Because
|
||||
Lektor has built-in support for deploying to GitHub Pages pairing up the three
|
||||
is a breeze.
|
||||
|
||||
We [created a guide :ref](../../../../docs/deployment/travisci/) and also recorded
|
||||
a screencast that goes with it:
|
||||
|
||||
<iframe width="100%" height=410 frameborder="0" allowfullscreen="allowfullscreen"
|
||||
src="https://www.youtube.com/embed/3pj_EyZIL5A?autoplay=0&fs=1">
|
||||
</iframe>
|
After Width: | Height: | Size: 272 KiB |
|
@ -0,0 +1,25 @@
|
|||
_model: page
|
||||
---
|
||||
title: Community
|
||||
---
|
||||
body:
|
||||
|
||||
#### banner ####
|
||||
image: header.jpg
|
||||
#### text-block ####
|
||||
text:
|
||||
|
||||
Working with Lektor is more fun when you can ask questions to other users.
|
||||
Because the project is so new there are not that many channels for it yet.
|
||||
Here is where you can find other Lektor users:
|
||||
|
||||
* [Find us on Stack Overflow](http://stackoverflow.com/questions/tagged/lektor)
|
||||
* [Ask us a question on Stack Overflow](http://stackoverflow.com/questions/ask?tags=lektor)
|
||||
* [Chat with us on Gitter :js-gitter-toggle-chat-button](https://gitter.im/lektor/lektor) (lektor/lektor)
|
||||
* Join the IRC Channel `#lektor` on `irc.freenode.net`
|
||||
* [Read the channel logs](https://botbot.me/freenode/lektor/)
|
||||
* [Join the web chat](https://webchat.freenode.net/?channels=lektor)
|
||||
|
||||
Want to get in contact with us otherwise?
|
||||
|
||||
* [Reach us via Twitter at @getlektor](https://twitter.com/getlektor)
|
After Width: | Height: | Size: 129 KiB |
|
@ -12,7 +12,8 @@ class: default
|
|||
text:
|
||||
|
||||
Lektor is an Open Source project licensed under the [three clause BSD
|
||||
license](../license/).
|
||||
license](../license/). For detailed copyright and license information
|
||||
see [License & Copyright](../license/).
|
||||
|
||||
## Imprint
|
||||
|
||||
|
|
Before Width: | Height: | Size: 337 KiB After Width: | Height: | Size: 175 KiB |
|
@ -7,9 +7,7 @@ body:
|
|||
#### banner ####
|
||||
image: header.jpg
|
||||
----
|
||||
height: 500
|
||||
----
|
||||
class: dark
|
||||
height: 500-tall
|
||||
----
|
||||
contents:
|
||||
|
||||
|
@ -18,26 +16,15 @@ contents:
|
|||
<div class="col-md-8">
|
||||
<img src="logo.png" alt="Lektor" class="logo">
|
||||
<p>
|
||||
a flexible and powerful static content management system for building
|
||||
A flexible and powerful static content management system for building
|
||||
complex and beautiful websites out of flat files — for people who do
|
||||
not want to make a compromise between a CMS and a static blog engine.
|
||||
<p>
|
||||
<a href="docs/what/">Read about what makes Lektor different</a>.
|
||||
Getting your ideas implemented is as easy as making breakfast eggs.
|
||||
</div>
|
||||
<div class="col-md-4 visible-md-block visible-lg-block">
|
||||
<div class="download-btn"><a href="download/"
|
||||
><i class="glyphicon glyphicon-download-alt"></i> Download</a></div>
|
||||
|
||||
<ul class="badges">
|
||||
<li><a href="https://twitter.com/getlektor"
|
||||
class="twitter-follow-button" data-show-count="false"
|
||||
data-size="large" data-dnt="true">Follow @getlektor</a>
|
||||
<li><a class="github-button" href="https://github.com/lektor/lektor"
|
||||
data-style="mega" data-count-href="/lektor/lektor/stargazers"
|
||||
data-count-api="/repos/lektor/lektor#stargazers_count"
|
||||
data-count-aria-label="# stargazers on GitHub"
|
||||
aria-label="Star lektor/lektor on GitHub">Star</a>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="install-row hide-for-windows">
|
||||
|
@ -46,13 +33,30 @@ contents:
|
|||
</div>
|
||||
#### text-block ####
|
||||
text:
|
||||
|
||||
<div class="badges">
|
||||
<ul>
|
||||
<li><a href="https://twitter.com/getlektor"
|
||||
class="twitter-follow-button" data-show-count="false"
|
||||
data-size="large" data-dnt="true">Follow @getlektor</a>
|
||||
<li><a class="github-button" href="https://github.com/lektor/lektor"
|
||||
data-style="mega" data-count-href="/lektor/lektor/stargazers"
|
||||
data-count-api="/repos/lektor/lektor#stargazers_count"
|
||||
data-count-aria-label="# stargazers on GitHub"
|
||||
aria-label="Star lektor/lektor on GitHub">Star</a>
|
||||
<li><a href="https://gitter.im/lektor/lektor"
|
||||
class="js-gitter-toggle-chat-button"><img
|
||||
src="https://badges.gitter.im/lektor/lektor.svg"
|
||||
alt="Join the chat at https://gitter.im/lektor/lektor"></a>
|
||||
</ul>
|
||||
</div>
|
||||
#### text-block ####
|
||||
text:
|
||||
## Full of Features
|
||||
|
||||
* <i class="feature-circle fa fa-ship"></i> **Deploy Anywhere** Because Lektor
|
||||
builds out 100% static HTML you can deploy them to any host (including S3,
|
||||
github pages, any web hoster, etc.)
|
||||
* <i class="feature-circle fa fa-globe"></i> **Cross Platform** it runs on
|
||||
github pages, any web host, etc.).
|
||||
* <i class="feature-circle fa fa-globe"></i> **Cross Platform** It runs on
|
||||
Linux, OSX and Windows.
|
||||
* <i class="feature-circle fa fa-file-code-o"></i> **100% File Based** All
|
||||
source data is well structured and can be tracked in a version control
|
||||
|
@ -60,21 +64,21 @@ text:
|
|||
* <i class="feature-circle fa fa-database"></i> **Flat-File Database** All
|
||||
source data is stored in a flat-file tree database and can be freely
|
||||
queried. The layout of that data is completely configurable.
|
||||
* <i class="feature-circle fa fa-pencil"></i> **Customizable Admin** comes
|
||||
fully equipped with a flexible and beautiful admin interface so you can
|
||||
* <i class="feature-circle fa fa-pencil"></i> **Customizable Admin** Lektor
|
||||
comes fully equipped with a flexible and beautiful admin interface so you can
|
||||
edit your pages without having to touch the raw sources.
|
||||
* <i class="feature-circle fa fa-code-fork"></i> **Dependency Tracking**
|
||||
The build process intelligently tracks dependencies between pages to only
|
||||
build the files that need rebuilding due to changes.
|
||||
* <i class="feature-circle fa fa-image"></i> **Image Tools** create
|
||||
The build process intelligently tracks page dependencies such that it only
|
||||
rebuilds pages that have changed.
|
||||
* <i class="feature-circle fa fa-image"></i> **Image Tools** Create
|
||||
thumbnails and give convenient access to EXIF data.
|
||||
* <i class="feature-circle fa fa-puzzle-piece"></i> **Plugin System** Lektor
|
||||
supports loading plugins that can customize a wide range of functionality.
|
||||
* <i class="feature-circle fa fa-terminal"></i> **Python API** the build
|
||||
* <i class="feature-circle fa fa-terminal"></i> **Python API** The build
|
||||
system is written in Python and provides a documented API to extend it and
|
||||
integrate into other apps.
|
||||
* <i class="feature-circle fa fa-language"></i> **Multilingual** We can
|
||||
speak multiple languages and allows you to easily create localized websites.
|
||||
speak multiple languages and allow you to easily create localized websites.
|
||||
----
|
||||
class: two-column-list
|
||||
#### slideshow ####
|
||||
|
|
|
@ -30,3 +30,5 @@ def build_stylesheet(artifact):
|
|||
with artifact.open('w') as f:
|
||||
f.write('Hello World!\n')
|
||||
```
|
||||
---
|
||||
module: lektor.builder
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
title: build_artifact
|
||||
---
|
||||
signature: artifact
|
||||
---
|
||||
summary: Invoked to build a previously declared artifact.
|
||||
---
|
||||
type: method
|
||||
---
|
||||
body:
|
||||
|
||||
This method is invoked for previously declared artifacts and is supposed to
|
||||
write out the artifact. It's only invoked if the builder decided that the
|
||||
artifact is outdated based on the information at hand.
|
||||
|
||||
For an example refer to the [add_build_program
|
||||
:ref](../../../environment/add-build-program/) documentation.
|
|
@ -0,0 +1,22 @@
|
|||
title: BuildProgram
|
||||
---
|
||||
module: lektor.build_programs
|
||||
---
|
||||
summary: The class for build programs.
|
||||
---
|
||||
type: class
|
||||
---
|
||||
body:
|
||||
|
||||
A build program is responsible for converting a [Source Object
|
||||
:ref](../../db/obj/) into final build artifacts. Typically such a build
|
||||
program implements two methods: [produce_artifacts :ref](produce-artifacts/)
|
||||
and [build_artifact :ref](build-artifact/).
|
||||
|
||||
The former should invoke [declare_artifact :ref](declare-artifact/) for each
|
||||
artifact that should be created from the source. The builder will then
|
||||
invoke [build_artifact :ref](build-artifact/) for each of these declared
|
||||
artifacts if the builder determiend that the artifact needs to be built.
|
||||
|
||||
For an example refer to the [add_build_program
|
||||
:ref](../../environment/add-build-program/) documentation.
|
|
@ -0,0 +1,23 @@
|
|||
title: declare_artifact
|
||||
---
|
||||
signature: artifact_name, sources=None, extra=None
|
||||
---
|
||||
summary: Declares an artifact to build from this program.
|
||||
---
|
||||
type: method
|
||||
---
|
||||
body:
|
||||
|
||||
This method is supposed to be called from [produce_artifacts
|
||||
:ref](../produce-artifacts/). For each of these invocations the builder will
|
||||
later invoke the [build_artifact :ref](../build-artifact/) function.
|
||||
|
||||
The parameters behave as follows:
|
||||
|
||||
* `artifact_name`: the name of the final artifact that will be built. This
|
||||
will be converted into a filename appropriate for the operating system
|
||||
automatically and should thus always use forward slashes.
|
||||
* `sources`: a list of source filenames that make up the artifact. This will
|
||||
be tracked as main indication about how artifacts change.
|
||||
* `extra`: arbitrary extra information that is associated with the artifact so
|
||||
that [build_artifact :ref](../build-artifact/) can use it.
|
|
@ -0,0 +1,19 @@
|
|||
title: iter_child_sources
|
||||
---
|
||||
type: method
|
||||
---
|
||||
summary: Optionally this method can be implemented to produce child sources.
|
||||
---
|
||||
body:
|
||||
|
||||
Optionally a builder can yield further sources that are then picked up by the
|
||||
builder and processed normally. This is how the recursive build process in
|
||||
Lektor is implemented for normal records.
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
def iter_child_sources(self):
|
||||
for child in self.sources.children:
|
||||
yield child
|
||||
```
|
|
@ -0,0 +1,17 @@
|
|||
title: produce_artifacts
|
||||
---
|
||||
summary: Method that needs to be overridden for the build to work.
|
||||
---
|
||||
type: method
|
||||
---
|
||||
body:
|
||||
|
||||
This method needs to be overridden by subclasses. It's invoked at the
|
||||
beginning of the build process and it's purpose is to invoke the
|
||||
[declare_artifact :ref](../declare-artifact/) method for each artifact
|
||||
that the build should generate. For each of these invocations later the
|
||||
[build_artifact :ref](../build-artifact/) method will be invoked if the
|
||||
builder determined that the artifact needs to be rebuild.
|
||||
|
||||
For an example refer to the [add_build_program
|
||||
:ref](../../../environment/add-build-program/) documentation.
|
|
@ -0,0 +1,10 @@
|
|||
title: source
|
||||
---
|
||||
summary: A reference to the source object.
|
||||
---
|
||||
type: property
|
||||
---
|
||||
body:
|
||||
|
||||
This refers to the source object that created the build program. It's primary
|
||||
use is to build the artifact in [build_artifact :ref](../build-artifact/).
|
|
@ -33,7 +33,7 @@ merged together. This means that if you have a file named `i18n.ini`
|
|||
with a section `[en]` and a key `CLICK_HERE` the path `i18n.en.CLICK_HERE`
|
||||
will target that key. For JSON files further nesting is possible. You
|
||||
can also just target a section and the return value will be a dictionary
|
||||
with can for instance be used with the [tojson :ref](../templates/filters/tojson/)
|
||||
which can for instance be used with the [tojson :ref](../templates/filters/tojson/)
|
||||
filter.
|
||||
|
||||
## Example Databag
|
||||
|
|
|
@ -29,22 +29,35 @@ Most plugins will not have source objects that actually originate on the
|
|||
file system. This means that their "source" is entirely virtual. Because
|
||||
this is a very common situation there is a base class, the
|
||||
`VirtualSourceObject` which plugins can subclass. The constructor takes one
|
||||
argument which is the parent source object the virtual source lives below.
|
||||
argument which is the source record the virtual source lives below. Virtual
|
||||
sources are separated from the records they belong to with an at sign (`@`)
|
||||
in the path. The part after the at sign is called the “virtual path”.
|
||||
|
||||
For instance in the below example the canonical path for the object would be
|
||||
the record's path + `@source`. So if the record was `/hello` then the
|
||||
path would be `/hello@source`. The true base record it belongs to can be
|
||||
referenced from the [record :ref](record/) property.
|
||||
|
||||
```python
|
||||
from lektor.sourceobj import VirtualSourceObject
|
||||
from lektor.utils import build_url
|
||||
|
||||
class Source(VirtualSourceObject):
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
return self.record.path + '@source'
|
||||
|
||||
@property
|
||||
def source_content(self):
|
||||
with open(self.parent.source_filename) as f:
|
||||
with open(self.record.source_filename) as f:
|
||||
return f.read().decode('utf-8')
|
||||
|
||||
@property
|
||||
def url_path(self):
|
||||
return self.parent.url_path + 'source.txt'
|
||||
return build_url([self.record.url_path, 'source.txt'])
|
||||
```
|
||||
|
||||
For more information see [add-build-program
|
||||
:ref](../../environment/add-build-program/).
|
||||
:ref](../../environment/add-build-program/) as well as
|
||||
[virtualpathresolver :ref](../../environment/virtualpathresolver/).
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
title: is_discoverable
|
||||
---
|
||||
summary: Indicates that a record can be discovered via .children
|
||||
---
|
||||
type: property
|
||||
---
|
||||
version_added: 2.0
|
||||
---
|
||||
body:
|
||||
|
||||
Records can be discoverable or non discoverable. This is controlled by the
|
||||
`_discoverable` system field which defaults to `yes. This property can quickly
|
||||
check if a record is considered non-discoverable by Lektor or not. In
|
||||
particular a hidden record is also considered undiscoverable.
|
||||
|
||||
Undiscoverable records can be queried and final pages will be built but they
|
||||
are excempt from queries of the record. This is useful for pages that should
|
||||
be built but not show up yet on overview pages. For instance this can be used
|
||||
to implement drafts of blog posts or similar things.
|
||||
|
||||
This property is implemented on the level of source objects to make it
|
||||
possible to use this API in all cases. The default implementation of
|
||||
source objects will always return `true` for discoverability checks.
|
||||
|
||||
## Example
|
||||
|
||||
```html+jinja
|
||||
{% set downloads = site.get('/downloads') %}
|
||||
{% if downloads.is_discoverable %}
|
||||
<p><a href="{{ downloads|url }}">Go to downloads</a>
|
||||
{% endif %}
|
||||
```
|
|
@ -0,0 +1,13 @@
|
|||
title: is_undiscoverable
|
||||
---
|
||||
summary: The inverse of is_discoverable.
|
||||
---
|
||||
type: property
|
||||
---
|
||||
version_added: 2.0
|
||||
---
|
||||
body:
|
||||
|
||||
This works like [is_discoverable :ref](../is-discoverable/) but the other
|
||||
way around. It just exists for consistency and to make checks look a little
|
||||
bit nicer in some places.
|
|
@ -0,0 +1,28 @@
|
|||
title: record
|
||||
---
|
||||
summary: A reference to the associated base record.
|
||||
---
|
||||
type: property
|
||||
---
|
||||
version_added: 2.0
|
||||
---
|
||||
body:
|
||||
|
||||
Most records (other than the root) have a [parent :ref](../parent/). This as
|
||||
a concept generally makes in most situations. However when virtual sources
|
||||
come into play the parent often just refers to another virtual source but not
|
||||
the root record they all belong to.
|
||||
|
||||
This property for the most part refers back to the object itself. However
|
||||
for virtual sources this refers back to the record that was associated with
|
||||
the virtual source. Most of the time this matches `parent` but for nested
|
||||
source objects it's useful to be able to refer back to the base in all cases
|
||||
without having to walk up the parents.
|
||||
|
||||
## Example
|
||||
|
||||
```html+jinja
|
||||
{% if this != this.record %}
|
||||
<a href="{{ this.record|url }}">go back to overview</a>
|
||||
{% endif %}
|
||||
```
|
|
@ -17,7 +17,8 @@ The path needs to be absolute with folder separated by slashes.
|
|||
|
||||
The default behavior is to load the unpaginated version of a record. If you
|
||||
want to select a specific page for pagination, then you need to pass
|
||||
`page_num` with a valid page number.
|
||||
`page_num` with a valid page number or you use the virtual path (`@1` for the
|
||||
first page for instance).
|
||||
|
||||
## Examples
|
||||
|
||||
|
@ -34,3 +35,15 @@ Here another example that loads the current page but in another language:
|
|||
{% set other_lang = site.get(this._path, alt='ru') %}
|
||||
<p>This page in Russian: {{ other_lang.title }}
|
||||
```
|
||||
|
||||
## Virtual Paths
|
||||
|
||||
This method can also be used to look up [virtual paths
|
||||
:ref](../../../../content/paths/). For instance to fetch a specific version of a
|
||||
pagination you can use a virtual path instead of using the `page_num`
|
||||
parameter:
|
||||
|
||||
```pycon
|
||||
>>> pad.get('/blog@3')
|
||||
<Page model=u'blog' path='/blog' page_num=3>
|
||||
```
|
||||
|
|
|
@ -17,7 +17,7 @@ and will also include hidden pages.
|
|||
|
||||
```html+jinja
|
||||
<ul>
|
||||
{% for project in site.query('/projects') %]
|
||||
{% for project in site.query('/projects') %}
|
||||
<li>{{ project.name }}: {{ project.year }}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
title: distinct
|
||||
---
|
||||
summary: Returns the set of unique values for a field.
|
||||
---
|
||||
type: method
|
||||
---
|
||||
signature: field_name
|
||||
---
|
||||
version_added: 2.0
|
||||
---
|
||||
body:
|
||||
|
||||
Returns a `set` with all values for `field_name` of all
|
||||
[Records :ref](../../record/) in this query.
|
||||
|
||||
## Example
|
||||
|
||||
If your blog posts have a field called `tags`:
|
||||
|
||||
```ini
|
||||
# blog-post.ini
|
||||
|
||||
[field.tags]
|
||||
name = Tags
|
||||
type = strings
|
||||
```
|
||||
|
||||
You can display all your blog posts' tags with:
|
||||
|
||||
```html+jinja
|
||||
{% set tags = site.query('/blog').distinct('tags') %}
|
||||
{% if tags %}
|
||||
<ul>
|
||||
{% for tag in tags|sort %}
|
||||
<li>{{ tag }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
```
|
|
@ -8,9 +8,9 @@ signature: expr
|
|||
---
|
||||
body:
|
||||
|
||||
This filters a query further down by an [Expression :ref](../../expr/).
|
||||
This filters a query further down by an [Expression :ref](../../expression/).
|
||||
Most expressions involve filtering by fields through [F :ref](../../f/)
|
||||
which allows you to perform comparisions with values that fields have.
|
||||
which allows you to perform comparisons with values that fields have.
|
||||
|
||||
Multiple filter calls can be chained as an alternative to using an `and`
|
||||
expression.
|
||||
|
|
|
@ -10,8 +10,9 @@ body:
|
|||
|
||||
This controls how the query should behave with regards to hidden records.
|
||||
A query created from the [children :ref](../../record/children/) attribute of
|
||||
a record will not include hidden records by default. The opposite is true
|
||||
for queries created from the [query :ref](../../pad/query/) method of the pad.
|
||||
a record will not include hidden records (or undiscoverable) by default. The
|
||||
opposite is true for queries created from the [query :ref](../../pad/query/)
|
||||
method of the pad.
|
||||
|
||||
The parameter can be set to `True` to include hidden or `False` to exclude
|
||||
hidden records.
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
title: include_undiscoverable
|
||||
---
|
||||
signature: value
|
||||
---
|
||||
summary: Controls what happens to records that are not discoverable.
|
||||
---
|
||||
type: method
|
||||
---
|
||||
version_added: 2.0
|
||||
---
|
||||
body:
|
||||
|
||||
This controls how the query should behave with regards to undiscoverable
|
||||
records. A query (either created from the [children :ref](../../record/children/)
|
||||
attribute or the [query :ref](../../pad/query/) method of the pad) will not
|
||||
include undiscoverable records by default.
|
||||
|
||||
If undiscoverable records should included this method needs to be used.
|
||||
Set it to `True` to include hidden or `False` to exclude them (default).
|
||||
|
||||
## Example
|
||||
|
||||
Here a basic example of how to filter something in a template:
|
||||
|
||||
```html+jinja
|
||||
<ul>
|
||||
{% for item in this.children.include_undiscoverable(true) %}
|
||||
<li>{{ item.title }}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
```
|
|
@ -0,0 +1,34 @@
|
|||
title: get_siblings
|
||||
---
|
||||
summary: The previous and next children of this page's parent.
|
||||
---
|
||||
type: method
|
||||
---
|
||||
body:
|
||||
|
||||
Get the previous and next record in this record's parent's list of children.
|
||||
|
||||
The returned object has attributes `prev_page` and `next_page`.
|
||||
Each can be a [Record :ref](../) or `None`.
|
||||
|
||||
If the parent record has pagination enabled, then use the pagination query to
|
||||
filter and order the children. Otherwise, the parent's standard configuration
|
||||
for children is used.
|
||||
See [the pagination guide :ref](../../../../guides/pagination/) and the
|
||||
[page order guide :ref](../../../../guides/page-order/).
|
||||
|
||||
## Example
|
||||
|
||||
```html+jinja
|
||||
{% set siblings = this.get_siblings() %}
|
||||
|
||||
{% if siblings.prev_page %}
|
||||
<a href="{{ siblings.prev_page|url }}">previous</a>
|
||||
{% endif %}
|
||||
|
||||
{% if siblings.next_page %}
|
||||
<a href="{{ siblings.next_page|url }}">next</a>
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
See also: [has_prev :ref](../has_prev/) and [has_next :ref](../has_next/).
|
|
@ -0,0 +1,22 @@
|
|||
title: []
|
||||
---
|
||||
summary: Looks up a field from the type.
|
||||
---
|
||||
type: operator
|
||||
---
|
||||
body:
|
||||
|
||||
The “get-item” operator is used to look up a field from a record. Within
|
||||
templates attribute access automatically falls back to this operator which
|
||||
is why you can typically access `foo.bar` instead of `foo['bar']` unless a
|
||||
conflict with a record property exists.
|
||||
|
||||
All available fields can be accessed this way (model defined fields as well
|
||||
as system fields which are prefixed by an underscore).
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
for child in this.children:
|
||||
print 'ID: %s' % child['_id']
|
||||
```
|
|
@ -0,0 +1,27 @@
|
|||
title: has_next
|
||||
---
|
||||
summary: Whether the record has a next sibling.
|
||||
---
|
||||
type: method
|
||||
---
|
||||
body:
|
||||
|
||||
True if there is a next record in the parent's list of children.
|
||||
|
||||
If the parent record has pagination enabled, then use the pagination query to
|
||||
filter and order the children. Otherwise, the parent's standard configuration
|
||||
for children is used.
|
||||
See [the pagination guide :ref](../../../../guides/pagination/) and the
|
||||
[page order guide :ref](../../../../guides/page-order/).
|
||||
|
||||
## Example
|
||||
|
||||
```html+jinja
|
||||
{% if this.has_next() %}
|
||||
<a href="{{ this.get_siblings().next_page|url }}">next</a>
|
||||
{% else %}
|
||||
<p>This is the last entry.
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
See also: [has_prev :ref](../has_prev/) and [get_siblings :ref](../get_siblings/).
|
|
@ -0,0 +1,27 @@
|
|||
title: has_prev
|
||||
---
|
||||
summary: Whether the record has a previous sibling.
|
||||
---
|
||||
type: method
|
||||
---
|
||||
body:
|
||||
|
||||
True if there is a previous record in the parent's list of children.
|
||||
|
||||
If the parent record has pagination enabled, then use the pagination query to
|
||||
filter and order the children. Otherwise, the parent's standard configuration
|
||||
for children is used.
|
||||
See [the pagination guide :ref](../../../../guides/pagination/) and the
|
||||
[page order guide :ref](../../../../guides/page-order/).
|
||||
|
||||
## Example
|
||||
|
||||
```html+jinja
|
||||
{% if this.has_prev() %}
|
||||
<a href="{{ this.get_siblings().prev_page|url }}">previous</a>
|
||||
{% else %}
|
||||
<p>This is the first entry.
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
See also: [has_next :ref](../has_next/) and [get_siblings :ref](../get_siblings/).
|
|
@ -15,17 +15,24 @@ overview page.
|
|||
|
||||
The following attributes exist on the pagination object:
|
||||
|
||||
| Attribute | Description
|
||||
| ----------- | ------------
|
||||
| `current` | The current record
|
||||
| `prev` | The record for the last page (might be `None`)
|
||||
| `next` | The record for the net page (might be `None`)
|
||||
| `total` | The total number of items across all pages
|
||||
| `pages` | The total number of pages
|
||||
| `page` | The number of current page
|
||||
| `has_prev` | `True` if a previous page exists
|
||||
| `has_next` | `True` if a next page exists
|
||||
| `items` | The query that resolves to the children of the current page.
|
||||
| Attribute | Description
|
||||
| ------------ | ------------
|
||||
| `current` | The current record
|
||||
| `prev` | The record for the last page (might be `None`)
|
||||
| `next` | The record for the net page (might be `None`)
|
||||
| `total` | The total number of items across all pages
|
||||
| `pages` | The total number of pages
|
||||
| `page` | The number of current page
|
||||
| `has_prev` | `True` if a previous page exists
|
||||
| `has_next` | `True` if a next page exists
|
||||
| `items` | The query that resolves to the children of the current page.
|
||||
| `for_page()` | Returns the record for a different page.
|
||||
|
||||
The `for_page()` function accepts a page number and returns the record for
|
||||
the other page. For more information also see the virtual path example
|
||||
below.
|
||||
|
||||
!! *Changed in Lektor 2.0:* The `for_page()` method was added.
|
||||
|
||||
## Item Query Example
|
||||
|
||||
|
@ -60,3 +67,23 @@ next page link as well as the number of the current page:
|
|||
{% endif %}
|
||||
</div>
|
||||
```
|
||||
|
||||
## Virtual Paths
|
||||
|
||||
!! *New in Lektor 2.0:* virtual paths did not exist in earlier Lektor versions.
|
||||
|
||||
The pagination is implemented in a way where each page in the pagination is
|
||||
a virtual path below the record itself. The value of the path is just the
|
||||
number of the page. So for instance to link to the second page you can just
|
||||
do this:
|
||||
|
||||
```html+jinja
|
||||
<a href="{{ '@2'|url }}">Go to Page 2</a>
|
||||
```
|
||||
|
||||
Alternatively you can also use the `for_path()` function which returns the
|
||||
entire pagination for a page:
|
||||
|
||||
```html+jinja
|
||||
<a href="{{ this.pagination.for_page(2)|url }}">Go to Page 2</a>
|
||||
```
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
title: parent
|
||||
---
|
||||
summary: Returns the parent record for a record.
|
||||
---
|
||||
type: property
|
||||
---
|
||||
body:
|
||||
|
||||
Because Lektor's database is a tree all records with the exception of the
|
||||
root record have a parent. This can be accessed with this property.
|
||||
|
||||
## Example
|
||||
|
||||
```html+jinja
|
||||
<a href="{{ this.parent|url }}">Up to {{ this.parent.title }}</a>
|
||||
```
|
|
@ -0,0 +1,11 @@
|
|||
title: _alt
|
||||
---
|
||||
summary: The alt for this record.
|
||||
---
|
||||
type: sysfield
|
||||
---
|
||||
body:
|
||||
|
||||
This field points to the [Alternative :ref](../../../../content/alts/) of
|
||||
the page. This should be considered as an internal field that is exposed
|
||||
through the [alt :ref](../../obj/alt/) source object property instead.
|
|
@ -0,0 +1,14 @@
|
|||
title: _attachment_type
|
||||
---
|
||||
summary: An indication of the attachment type.
|
||||
---
|
||||
type: sysfield
|
||||
---
|
||||
body:
|
||||
|
||||
This indicates the type of the attachment. Currently only a small set of
|
||||
attachments is detected. The most useful attachment type is `image` which
|
||||
identifies images. The attachments are detected by the file extension.
|
||||
|
||||
!!!! This feature in many ways does not entirely work as you would expect
|
||||
and should be taken is something that will improve in future versions.
|
|
@ -0,0 +1,13 @@
|
|||
title: System Fields
|
||||
---
|
||||
summary: The complete list of system fields in Lektor
|
||||
---
|
||||
body:
|
||||
|
||||
All records have a few system fields available in addition to the fields
|
||||
defined by the data model. These fields are always there and control internal
|
||||
behavior in Lektor. They are prefixed by an underscore to separate them
|
||||
from the fields a model defines.
|
||||
|
||||
Many system fields are hidden from the admin panel but some can be changed
|
||||
there (`_template`, `_hidden`, and a few others).
|
|
@ -0,0 +1,20 @@
|
|||
title: _discoverable
|
||||
---
|
||||
summary: Controls if this page is picked up by collection queries by default.
|
||||
---
|
||||
type: sysfield
|
||||
---
|
||||
version_added: 2.0
|
||||
---
|
||||
body:
|
||||
|
||||
By default any non hidden page is returned from `.children` on iteration. This
|
||||
field can be set to `no` to hide a page from such default queries. This
|
||||
implicitly hides a page for most template operations but Lektor will still
|
||||
build it. This for instance is very useful for draft blog posts. If a post is
|
||||
set to not being discoverable it will be hidden from the blog index without
|
||||
further custom template code, but someone who knows the URL can still find it.
|
||||
|
||||
This is also particularly useful to hide special pages such as `sitemap.xml`
|
||||
which would otherwise cause problems because generic code might not expect
|
||||
it.
|
|
@ -0,0 +1,20 @@
|
|||
title: _gid
|
||||
---
|
||||
summary: A globally unique ID of the page.
|
||||
---
|
||||
type: sysfield
|
||||
---
|
||||
body:
|
||||
|
||||
The `_gid` is the MD5 hashed version of the page's [`_path`](../path/). This
|
||||
is useful in situations where a stable ID is needed that follows a certain
|
||||
format. For instance it can come in useful when a ID is needed for a DOM
|
||||
element.
|
||||
|
||||
## Example
|
||||
|
||||
```html+jinja
|
||||
<body class="page-{{ this._gid }}">
|
||||
...
|
||||
</body>
|
||||
```
|
|
@ -0,0 +1,20 @@
|
|||
title: _hidden
|
||||
---
|
||||
summary: Controls if the page should be built or not.
|
||||
---
|
||||
type: sysfield
|
||||
---
|
||||
body:
|
||||
|
||||
This field controls if Lektor should process the page into a build artifact.
|
||||
By default each page is built into a build artifact (HTML page) and each
|
||||
attachment is processed. This can be prevented by setting `_hidden` to `yes`.
|
||||
|
||||
This also automatically applies to all children of a page unless they
|
||||
forcefully override this setting.
|
||||
|
||||
This is useful for more advanced setups like [Single Page Applications
|
||||
:ref](../../../../guides/single-page/).
|
||||
|
||||
Hidden pages are automatically also removed from the `.children` property
|
||||
of records but stay available for querying via the pad.
|
|
@ -0,0 +1,32 @@
|
|||
title: _id
|
||||
---
|
||||
summary: The local identifier of a record.
|
||||
---
|
||||
type: sysfield
|
||||
---
|
||||
body:
|
||||
|
||||
Each record has an `_id`. This ID is basically a form of the filename.
|
||||
Depending on if you are looking at an attachment or a page the rules are
|
||||
slightly different.
|
||||
|
||||
For pages the ID is the name of the folder. So if you have a page called
|
||||
`docs/overview/contents.lr` then `_id` is `overview`. If you have however
|
||||
an attachment named `docs/overview/screenshot.jpg` the `_id` will be the
|
||||
filename of the attachment: `screenshot.jpg`.
|
||||
|
||||
Note that IDs are not globally unique! There is also the `_path` which is
|
||||
the entire path if the record.
|
||||
|
||||
The `_id` is automatically set and cannot be overridden.
|
||||
|
||||
## Example
|
||||
|
||||
```html+jinja
|
||||
<ul class="nav">
|
||||
{% for item in site.query('/projects') %}
|
||||
<li{% if item._id == this._id %} class="active"{%
|
||||
endif %}>{{ item.name }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
```
|
|
@ -0,0 +1,23 @@
|
|||
title: _model
|
||||
---
|
||||
summary: The model the record uses for its fields.
|
||||
---
|
||||
type: sysfield
|
||||
---
|
||||
body:
|
||||
|
||||
This field selects the model that should be used for non-system fields. In
|
||||
many situations the model is picked automatically but for equally many
|
||||
situations it needs to be selected manually.
|
||||
|
||||
This field is most useful for filtering when operating on mixed collections.
|
||||
|
||||
## Example
|
||||
|
||||
```html+jinja
|
||||
<ul class="projects">
|
||||
{% for child in this.children.filter(F._model == 'project') %}
|
||||
<li>{{ child.name }}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
```
|
|
@ -0,0 +1,22 @@
|
|||
title: _path
|
||||
---
|
||||
summary: The full path of the record.
|
||||
---
|
||||
type: sysfield
|
||||
---
|
||||
body:
|
||||
|
||||
The `_path` is the more complete version of the [`_id`](../id/). It contains
|
||||
the entire path of records that are the parents of a record. So if you have a
|
||||
record named `docs/api/db/contents.lr` the `_id` would be `db` but the `_path`
|
||||
would be `docs/api/db`.
|
||||
|
||||
The path can be used to uniquely identify a page but for that purpose the
|
||||
[`_gid`](../gid/) can also be used which is a hashed hexadecimal version of
|
||||
the page.
|
||||
|
||||
## Example
|
||||
|
||||
```html+jinja
|
||||
<!-- generated from {{ this._path }} -->
|
||||
```
|
|
@ -0,0 +1,13 @@
|
|||
title: _slug
|
||||
---
|
||||
summary: The URL slug of the record.
|
||||
---
|
||||
type: sysfield
|
||||
---
|
||||
body:
|
||||
|
||||
This field defines the URL slug of the record. If not set it defaults to
|
||||
either the `_id` or an expansion of what the parent defines for all children.
|
||||
|
||||
For more information about this refer to [URLs and Slugs
|
||||
:ref](../../../../content/urls/).
|
|
@ -0,0 +1,20 @@
|
|||
title: _source_alt
|
||||
---
|
||||
summary: The alt from the source of the record.
|
||||
---
|
||||
type: sysfield
|
||||
---
|
||||
body:
|
||||
|
||||
This field points to the true source [Alternative
|
||||
:ref](../../../../content/alts/) of the page. This primarily exists internally
|
||||
to support the builder. In particular the difference to the `_alt` field is
|
||||
that this one will indicate if an alt falls back to a different alternative.
|
||||
At present pages can only fall back to the `_primary` alternative which will
|
||||
be reflected by this field.
|
||||
|
||||
## Example
|
||||
|
||||
```html+jinja
|
||||
<!-- generated from language {{ this._source_alt }} -->
|
||||
```
|
|
@ -0,0 +1,13 @@
|
|||
title: _template
|
||||
---
|
||||
signature:
|
||||
---
|
||||
summary: Selects the template for the page.
|
||||
---
|
||||
type: sysfield
|
||||
---
|
||||
body:
|
||||
|
||||
This field sets the template that Lektor uses for rendering. It defaults to
|
||||
model name + `.html`. If the `_model` is `page` the name of the template
|
||||
will be `page.html`. In some situations it makes sense to override this.
|
|
@ -0,0 +1,46 @@
|
|||
title: Type
|
||||
---
|
||||
module: lektor.types
|
||||
---
|
||||
signature: env, options
|
||||
---
|
||||
summary: The base class for all field types.
|
||||
---
|
||||
type: class
|
||||
---
|
||||
version_added: 2.0
|
||||
---
|
||||
body:
|
||||
|
||||
The fields in [Records :ref](../record/) use types to specify the behavior of
|
||||
the values. Lektor comes with a wide range of [built-in field types
|
||||
:ref](../types/) but it is possible to build your own by subclassing types
|
||||
class. A type is instanciated with two parameters: a reference to the
|
||||
[Environment :ref](../../environment/) that it belongs to and a dictionary
|
||||
with configuration options from the ini file.
|
||||
|
||||
A field type has to implement the [value_from_raw :ref](value-from-raw/)
|
||||
method and set the `widget` property as a very basic requirement.
|
||||
|
||||
To create a type you need to create a subclass. The name of the class needs
|
||||
to match the type name. If you want to name your type `mything` then it
|
||||
needs to be called `MyThingType`. Afterwards you can register it with the
|
||||
environment in [setup_env :ref](../../plugins/events/setup-env/):
|
||||
|
||||
```python
|
||||
from lektor.types import Type
|
||||
|
||||
class MyThingType(Type):
|
||||
widget = 'singleline-text'
|
||||
|
||||
def value_from_raw(self, raw):
|
||||
return raw.value
|
||||
|
||||
def setup_env(self, **extra):
|
||||
self.env.add_type(MyThingType)
|
||||
```
|
||||
|
||||
For more information see [value_from_raw :ref](value-from-raw/).
|
||||
|
||||
There is a more complete example in the
|
||||
[Plugin How To :ref](../../../plugins/howto/#adding-new-field-types).
|
|
@ -0,0 +1,18 @@
|
|||
title: to_json
|
||||
---
|
||||
signature: pad, record=None, alt='_primary'
|
||||
---
|
||||
summary: Returns the type information as JSON.
|
||||
---
|
||||
template_var:
|
||||
---
|
||||
type: method
|
||||
---
|
||||
version_added: 2.0
|
||||
---
|
||||
body:
|
||||
|
||||
This method is used to export a type definition to JSON. Primarily this is
|
||||
used to drive some more special widgets for the admin UI. For the moment this
|
||||
is largely undocumented as widgets are not yet customizable. For more
|
||||
information you will need to read the Lektor docs.
|
|
@ -0,0 +1,52 @@
|
|||
title: value_from_raw
|
||||
---
|
||||
signature: raw
|
||||
---
|
||||
summary: Main method to override to implement the value conversion.
|
||||
---
|
||||
type: method
|
||||
---
|
||||
version_added: 2.0
|
||||
---
|
||||
body:
|
||||
|
||||
This method needs to be implemented to perform the type conversion from the
|
||||
raw value from the content file into a value that Lektor can process in the
|
||||
database layer. This can be any Python type for as long as it makes sense.
|
||||
It must either return a valid value or a special value that indicates a bad
|
||||
value. For more information see the raw value information below and the
|
||||
example provided.
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from lektor.types import Type
|
||||
|
||||
class LocationType(Type):
|
||||
widget = 'singleline-text'
|
||||
|
||||
def value_from_raw(self, raw):
|
||||
if raw.value is None:
|
||||
return raw.missing_value('Location was not supplied')
|
||||
try:
|
||||
lng, lat = [float(x.strip()) for x in
|
||||
raw.value.split(';')]
|
||||
except (TypeError, ValueError):
|
||||
return raw.bad_value('Location was malformed')
|
||||
return (lng, lat)
|
||||
|
||||
def setup_env(self, **extra):
|
||||
self.env.add_type(LocationType)
|
||||
```
|
||||
|
||||
## Raw Value
|
||||
|
||||
The value passed is a `raw` value type. It has a few properties and methods:
|
||||
|
||||
| Attribute | Description
|
||||
| -------------------- | --------------------
|
||||
| `name` | The name of the field
|
||||
| `value` | The raw value as unicode string or `None` if missing
|
||||
| `pad` | A reference to the [pad :ref](../../pad/)
|
||||
| `bad_value(rsn)` | Creates a value that indicates a bad value with a reason.
|
||||
| `missing_value(rsn)` | Similar to `bad_value `but indicates an absent value.
|
|
@ -0,0 +1,47 @@
|
|||
title: widget
|
||||
---
|
||||
summary: An attribute that identifies the widget to use in the admin panel.
|
||||
---
|
||||
type: property
|
||||
---
|
||||
version_added: 2.0
|
||||
---
|
||||
body:
|
||||
|
||||
It's currently not yet possible to create your own widgets for the admin panel
|
||||
but you can select one of the built-in widgets for your own type. Note that
|
||||
not all widgets will necessarily will work directly with your type as such.
|
||||
For instance the `select` widget and some others will currently require some
|
||||
extra values be supplied in the [to_json :ref](../to-json/) method.
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from lektor.types import Type
|
||||
|
||||
class MyThingType(Type):
|
||||
widget = 'singleline-text'
|
||||
|
||||
def value_from_raw(self, raw):
|
||||
return raw.value
|
||||
```
|
||||
|
||||
## Available Widgets
|
||||
|
||||
These widgets are currently available:
|
||||
|
||||
* `singleline-text`: single-line text input field
|
||||
* `multiline-text`: multi-line text input field
|
||||
* `datepicker`: a date input field (currently not yet an actual date picker)
|
||||
* `integer`: an integer input field
|
||||
* `float`: a floating point value input field
|
||||
* `checkbox`: a checkbox
|
||||
* `url`: a URL input field
|
||||
* `slug`: input field for URL slugs
|
||||
* `checkboxes`: an input field with multiple checkboxes *
|
||||
* `select`: an input field in the form of a select box *
|
||||
|
||||
\* `checkboxes` and `select` require a `choices` list to be supplied which is
|
||||
a list of `[value, label]` tuples where value is the stored value and label
|
||||
is a dictionary of internationalized values for the UI. For more information
|
||||
you will currently have to refer to the Lektor sourcecode.
|
|
@ -1,9 +1,10 @@
|
|||
title: Field Types
|
||||
title: Builtin Field Types
|
||||
---
|
||||
summary: An overview of all the field types supported in Lektor.
|
||||
---
|
||||
body:
|
||||
|
||||
Lektor supports many different types that can be used for data modelling.
|
||||
Currently it's not possible to extend these but the intention exists to make
|
||||
this even more configurable from plugins in the future.
|
||||
|
||||
Plugins can add additional types, see the
|
||||
[plugin "how to" :ref](../../../plugins/howto/#adding-new-field-types).
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
title: datetime
|
||||
---
|
||||
type: type
|
||||
---
|
||||
summary: A type that can store a date and time.
|
||||
---
|
||||
body:
|
||||
|
||||
The `datetime` type can store a date and time.
|
||||
`datetime` value can also include time zone info.
|
||||
|
||||
The canonical format for the type in text form is `YYYY-MM-DD HH:MM:SS[ Z]`.
|
||||
|
||||
`Z` is optional information.
|
||||
You can use `Z` field with below informations.
|
||||
|
||||
* Time Zone Abbreviations such as `UTC`, `EST`
|
||||
* Time Zone Name such as `America/Dominica`
|
||||
* Timedelta such as `+0900`, `-0930`
|
||||
|
||||
You can also use [datetimeformat
|
||||
:ref](../../../../api/templates/filters/datetimeformat/)
|
||||
filter with this type.
|
||||
|
||||
## Valid Examples
|
||||
|
||||
```
|
||||
pub_date: 2016-01-13 07:53:22
|
||||
|
||||
or
|
||||
|
||||
pub_date: 2016-01-13 07:53:22 UTC
|
||||
|
||||
or
|
||||
|
||||
pub_date: 2016-01-13 07:53:22 EST
|
||||
|
||||
or
|
||||
|
||||
pub_date: 2016-01-13 07:53:22 America/Dominica
|
||||
|
||||
or
|
||||
|
||||
pub_date: 2016-01-13 07:53:22 +0900
|
||||
```
|
||||
|
||||
## Field Usage
|
||||
|
||||
```ini
|
||||
[fields.pub_date]
|
||||
label = Publication date
|
||||
type = datetime
|
||||
```
|
||||
|
||||
## Template Usage
|
||||
|
||||
```html+jinja
|
||||
<p>Published: {{ this.pub_date.strftime('%Y-%m-%d %H:%M:%S') }}
|
||||
```
|
|
@ -0,0 +1,21 @@
|
|||
title: sort_key
|
||||
---
|
||||
summary: An integer type specific for sorting.
|
||||
---
|
||||
type: type
|
||||
---
|
||||
body:
|
||||
|
||||
The `sort_key` type is similar to the `integer` type which is one of the most
|
||||
basic ones in Lektor. It can store arbitrary natural numbers both negative and
|
||||
positive. It's intended for giving pages a sort order. The reason it's a
|
||||
special type is that future versions of Lektor can take advantage of this to
|
||||
implement a widget for ordering.
|
||||
|
||||
## Field Usage
|
||||
|
||||
```ini
|
||||
[fields.sort_key]
|
||||
label = Sort order
|
||||
type = sort_key
|
||||
```
|
|
@ -19,17 +19,22 @@ artifacts out of source objects.
|
|||
```python
|
||||
from lektor.sourceobj import VirtualSourceObject
|
||||
from lektor.build_programs import BuildProgram
|
||||
from lektor.utils import build_url
|
||||
|
||||
class Source(VirtualSourceObject):
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
return self.record.path + '@source'
|
||||
|
||||
@property
|
||||
def source_content(self):
|
||||
with open(self.parent.source_filename) as f:
|
||||
with open(self.record.source_filename) as f:
|
||||
return f.read().decode('utf-8')
|
||||
|
||||
@property
|
||||
def url_path(self):
|
||||
return self.parent.url_path + 'source.txt'
|
||||
return build_url([self.record.url_path, 'source.txt'])
|
||||
|
||||
class SourceBuildProgram(BuildProgram):
|
||||
|
||||
|
@ -43,6 +48,11 @@ class SourceBuildProgram(BuildProgram):
|
|||
this=self.source)
|
||||
|
||||
env.add_build_program(Source, SourceBuildProgram)
|
||||
|
||||
@env.virtualpathresolver('source')
|
||||
def resolve_virtual_path(record, pieces):
|
||||
if not pieces:
|
||||
return Source(record)
|
||||
```
|
||||
|
||||
And here the example `view_source.html` template:
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
title: add_publisher
|
||||
---
|
||||
type: method
|
||||
---
|
||||
signature: scheme, publisher
|
||||
---
|
||||
summary: Registers a publisher class with the environment.
|
||||
---
|
||||
body:
|
||||
|
||||
This method can be used to register a new publisher for a given URL scheme
|
||||
with Lektor. This allows plugins to provide custom deployment methods. For
|
||||
more information on implementing these see [Publisher :ref](../../publisher/).
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from lektor.publisher import Publisher
|
||||
|
||||
class MyPublisher(Publisher):
|
||||
pass
|
||||
|
||||
env.add_publisher('my', MyPublisher)
|
||||
```
|
||||
---
|
||||
version_added: 2.0
|
|
@ -0,0 +1,28 @@
|
|||
title: add_type
|
||||
---
|
||||
signature: type
|
||||
---
|
||||
summary: Adds a new field type to the environment.
|
||||
---
|
||||
type: method
|
||||
---
|
||||
version_added: 2.0
|
||||
---
|
||||
body:
|
||||
|
||||
This registers a new [Field Type :ref](../../db/type/) with the environment
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from lektor.types import Type
|
||||
|
||||
class MyThingType(Type):
|
||||
widget = 'singleline-text'
|
||||
|
||||
def value_from_raw(self, raw):
|
||||
return raw.value
|
||||
|
||||
def setup_env(self, **extra):
|
||||
self.env.add_type(MyThingType)
|
||||
```
|
|
@ -20,12 +20,17 @@ exists for a page.
|
|||
```python
|
||||
from lektor.sourceobj import VirtualSourceObject
|
||||
from lektor.db import Record
|
||||
from lektor.utils import build_url
|
||||
|
||||
class Source(VirtualSourceObject):
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
return self.record.path + '@source'
|
||||
|
||||
@property
|
||||
def url_path(self):
|
||||
return self.parent.url_path + 'source.txt'
|
||||
return build_url([self.record.url_path, 'source.txt'])
|
||||
|
||||
@env.generator
|
||||
def generate_source_file(node):
|
||||
|
|
|
@ -19,12 +19,17 @@ URL path segments.
|
|||
|
||||
```python
|
||||
from lektor.sourceobj import VirtualSourceObject
|
||||
from lektor.utils import build_url
|
||||
|
||||
class Source(VirtualSourceObject):
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
return self.record.path + '@source'
|
||||
|
||||
@property
|
||||
def url_path(self):
|
||||
return self.parent.url_path + 'source.txt'
|
||||
return build_url([self.record.url_path, 'source.txt'])
|
||||
|
||||
@env.urlresolver
|
||||
def match_source_file(node, url_path):
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
title: virtualpathresolver
|
||||
---
|
||||
signature: prefix
|
||||
---
|
||||
summary: Registers a virtual path resolver with the environment.
|
||||
---
|
||||
type: method
|
||||
---
|
||||
version_added: 2.0
|
||||
---
|
||||
body:
|
||||
|
||||
When implementing [Virtual Source Objects :ref](../../db/obj/) it's important
|
||||
that you can locate them. While virtual sources do not appear as children of
|
||||
pages they can be navigated to through the database pad. This is achieved
|
||||
through custom virtual path resolvers.
|
||||
|
||||
Each source object needs to be a unique prefix which identifies all instances
|
||||
of it. For instance if you want to implement a special page (like a feed)
|
||||
that would exist for specific pages, then you can register the virtual path
|
||||
prefix `feed` with your plugin. Though you should use more descriptive names
|
||||
for your plugins as these paths are shared.
|
||||
|
||||
If a user then resolves the page `/my-page@feed` it would invoke your URL
|
||||
resolver with the record `my-page` and an empty list (rest of the path
|
||||
segments). If they would request `/my-page@feed/recent` then it would
|
||||
pass `['recent']` as path segments.
|
||||
|
||||
## Example
|
||||
|
||||
Here an example that would implement a virtual source for feeds and an
|
||||
associated virtual path resolver:
|
||||
|
||||
```python
|
||||
from lektor.sourceobj import VirtualSourceObject
|
||||
from lektor.utils import build_url
|
||||
|
||||
class Feed(VirtualSourceObject):
|
||||
|
||||
def __init__(self, record, version='default'):
|
||||
VirtualSourceObject.__init__(self, record)
|
||||
self.version = version
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
return '%s@feed%s' % (
|
||||
self.record.path,
|
||||
'/' + self.version if self.version != 'default' else '',
|
||||
)
|
||||
|
||||
@property
|
||||
def url_path(self):
|
||||
return build_url([self.record.url_path, 'feed.xml'])
|
||||
|
||||
|
||||
def on_setup_env(self, **extra):
|
||||
@self.env.virtualpathresolver('feed')
|
||||
def resolve_virtual_path(record, pieces):
|
||||
if not pieces:
|
||||
return Feed(record)
|
||||
elif pieces == ['recent']:
|
||||
return Feed(record, version='recent')
|
||||
```
|
|
@ -0,0 +1,12 @@
|
|||
title: after-build-all
|
||||
---
|
||||
signature: builder
|
||||
---
|
||||
summary: This event is emitted after all sources are built.
|
||||
---
|
||||
type: event
|
||||
---
|
||||
body:
|
||||
|
||||
This event is emitted after the builder build all sources. It works similar
|
||||
to the [before-build-all :ref](../before-build-all/) event otherwise.
|
|
@ -0,0 +1,13 @@
|
|||
title: after-build
|
||||
---
|
||||
signature: builder, build_state, source, prog
|
||||
---
|
||||
summary: This event is emitted after an individual source is built.
|
||||
---
|
||||
type: event
|
||||
---
|
||||
body:
|
||||
|
||||
This event is emitted right after a source is being built into a final build
|
||||
artifact. It works pretty much exactly the same as the [before-build
|
||||
:ref](../before-build/) event.
|
|
@ -0,0 +1,27 @@
|
|||
title: before-build-all
|
||||
---
|
||||
signature: builder
|
||||
---
|
||||
summary: This event is emitted before all sources are built.
|
||||
---
|
||||
type: event
|
||||
---
|
||||
body:
|
||||
|
||||
This event is emitted before the builder decides to build all sources. It's
|
||||
most useful for preprocessing sources on the file system before the rest of
|
||||
the build process should pick it up. For instance here a plugin could inject
|
||||
additional files into the asset folder and similar things.
|
||||
|
||||
As an example, this is what the [Webpack Support
|
||||
:ref](../../../../guides/webpack/) uses.
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
import subprocess
|
||||
|
||||
def on_before_build_all(self, builder, **extra):
|
||||
root = ospath.join(self.env.root_path, 'webpack')
|
||||
subprocess.Popen(['webpack'], cwd=root).wait()
|
||||
```
|
|
@ -0,0 +1,35 @@
|
|||
title: before-build
|
||||
---
|
||||
type: event
|
||||
---
|
||||
signature: builder, build_state, source, prog
|
||||
---
|
||||
summary: This event is emitted before an individual source is built.
|
||||
---
|
||||
body:
|
||||
|
||||
This event is emitted right before a source is being built into a final build
|
||||
artifact. Note that this event in itself does not indicate if the build will
|
||||
actually take place or not due to the artifact being current already as such
|
||||
the usefulness is limited.
|
||||
|
||||
The parameters being passed are:
|
||||
|
||||
* `builder`: a reference to the builder.
|
||||
* `build_state`: a reference to the build state object.
|
||||
* `source`: the source object that is being processed. (See
|
||||
[Source Object :ref](../../../db/obj/) for more information)
|
||||
* `prog`: the build program that is being used to process the source. (See
|
||||
[Build Program :ref](../../../build/program/) for more information)
|
||||
|
||||
!!!! Note that currently both the builder as well as the build state are
|
||||
undocumented and unsupported! This means that if a plugin choses to use those
|
||||
references to do something with it they should consider that they might break
|
||||
in future versions.
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
def on_before_build(self, source, prog, **extra):
|
||||
print 'building %s' % source.source_filename
|
||||
```
|
|
@ -6,5 +6,5 @@ summary: A reference to the active environment.
|
|||
---
|
||||
body:
|
||||
|
||||
This is just a reference back to the [Environment :ref](../../../env/)
|
||||
This is just a reference back to the [Environment :ref](../../../environment/)
|
||||
that the plugin belongs to.
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
title: Publisher
|
||||
---
|
||||
module: lektor.publisher
|
||||
---
|
||||
summary: The interface for extending the deployment process
|
||||
---
|
||||
type: class
|
||||
---
|
||||
body:
|
||||
|
||||
This class can be subclassed to implement custom deployment methods.
|
||||
Internally these are called “publishers” and to register them with the
|
||||
environment the [add_publisher :ref](../environment/add-publisher/) method can
|
||||
be used.
|
||||
|
||||
Publishers have one method called [publish :ref](publish/) which is used to
|
||||
trigger the actual deployment process. It also has a reference back to the
|
||||
environment it belongs to as well as the output path of the build process.
|
||||
|
||||
For a minimal example of a publisher refer to the documentation of the
|
||||
[publish :ref](publish/) method.
|
||||
---
|
||||
version_added: 2.0
|
|
@ -0,0 +1,12 @@
|
|||
title: env
|
||||
---
|
||||
summary: Reference to the creating environment
|
||||
---
|
||||
type: property
|
||||
---
|
||||
version_added: 2.0
|
||||
---
|
||||
body:
|
||||
|
||||
As each publisher is bound to an [Environment :ref](../../environment/) that
|
||||
created it. This is useful to discover related information.
|
|
@ -0,0 +1,31 @@
|
|||
title: fail
|
||||
---
|
||||
summary: Notifies a failure during publishing
|
||||
---
|
||||
signature: message
|
||||
---
|
||||
type: method
|
||||
---
|
||||
version_added: 2.0
|
||||
---
|
||||
body:
|
||||
|
||||
This method takes a message and raises an appropriate failure that aborts
|
||||
the publishing process. This is invoked from within the [publish
|
||||
:ref](../publish/) method to indicate a failure:
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from lektor.publisher import Publisher
|
||||
|
||||
|
||||
class MyPublisher(Publisher):
|
||||
def publish(self, target_url, credentials=None, **extra):
|
||||
self.fail('This publisher cannot publish :(')
|
||||
|
||||
|
||||
class MyPlugin(Plugin):
|
||||
def on_setup_env(self, **extra):
|
||||
self.env.add_publisher('my', MyPublisher)
|
||||
```
|
|
@ -0,0 +1,15 @@
|
|||
title: output_path
|
||||
---
|
||||
type: property
|
||||
---
|
||||
summary: The path to the folder with the build artifacts
|
||||
---
|
||||
version_added: 2.0
|
||||
---
|
||||
body:
|
||||
|
||||
This attribute holds the path to the folder where the build process put the
|
||||
final artifacts. Usually a publisher walks that folder and does something
|
||||
with all contents of it. The publishers however are heavily encourated to
|
||||
ignore the special `.lektor` folder which contains lektor specific information
|
||||
that should not end up on the resulting server.
|
|
@ -0,0 +1,82 @@
|
|||
title: publish
|
||||
---
|
||||
signature: target_url, credentials=None, **extra
|
||||
---
|
||||
summary: The method that triggers the deployment
|
||||
---
|
||||
type: method
|
||||
---
|
||||
version_added: 2.0
|
||||
---
|
||||
body:
|
||||
|
||||
This method implements the actual publishing process. It's supposed to
|
||||
implement a generator that reports the progress of the publishing. If at any
|
||||
point something happens that would cause an error for the deployment this can
|
||||
be signalled with the [fail :ref](../fail/) method which aborts the execution
|
||||
and reports an error.
|
||||
|
||||
The parameters to the function are as follows:
|
||||
|
||||
* `target_url`: a URL object with the parsed URL. This object comes from the
|
||||
Werkzeug library and gives access to the individual parts of a URL by the
|
||||
exposed attributes ([Read about the URL object :ext](http://werkzeug.pocoo.org/docs/0.11/urls/)).
|
||||
* `credentials`: an optional dictionary with command line supplied credentials.
|
||||
Note that these credentials might be completely absent and the keys which are
|
||||
provided might change with future versions of Lektor.
|
||||
* `**extra`: for forwards compatibility publishers are required to ignore extra
|
||||
keyword arguments.
|
||||
|
||||
Each line in the generator must be a string which is then either logged to
|
||||
the output in the console or in the deploy/publish window in the admin UI.
|
||||
|
||||
## Example
|
||||
|
||||
This example implements a simple publisher that just copies all built files
|
||||
into a new location.
|
||||
|
||||
```python
|
||||
import os
|
||||
import shutil
|
||||
from lektor.publisher import Publisher
|
||||
|
||||
|
||||
class CopyPublisher(Publisher):
|
||||
|
||||
def publish(self, target_url, credentials=None, **extra):
|
||||
src_path = self.output_path
|
||||
dst_path = target_url.path
|
||||
strip = len(src_path) + 1
|
||||
|
||||
for path, folders, filenames in os.walk(src_path):
|
||||
# Ignore the .lektor folder.
|
||||
folders[:] = [x for x in folders if x != '.lektor']
|
||||
|
||||
# Copy all files over
|
||||
for filename in filenames:
|
||||
full_path = os.path.join(src_path, path, filename)
|
||||
dst = os.path.join(path, full_path[strip:])
|
||||
|
||||
# Make sure the destination folder exists.
|
||||
try:
|
||||
os.makedirs(os.path.dirname(dst))
|
||||
except (OSError, IOError):
|
||||
pass
|
||||
|
||||
# Copy the file
|
||||
yield 'Copy %s' % filename
|
||||
shutil.copy(full_path, dst)
|
||||
|
||||
yield 'Done'
|
||||
|
||||
|
||||
class MyPlugin(Plugin):
|
||||
def on_setup_env(self, **extra):
|
||||
self.env.add_publisher('copy', CopyPublisher)
|
||||
```
|
||||
|
||||
This publisher registers with the `copy` scheme and could be used like this:
|
||||
|
||||
```ini
|
||||
target_url = copy:///path/to/destination/folder
|
||||
```
|
|
@ -0,0 +1,41 @@
|
|||
title: datetimeformat
|
||||
---
|
||||
type: filter
|
||||
---
|
||||
signature: datetime, format='medium', locale=None
|
||||
---
|
||||
summary: Formats a datetime into a string
|
||||
---
|
||||
body:
|
||||
|
||||
To format a proper date (this is created by the [datetime
|
||||
:ref](../../../../api/db/types/datetime/) type) into a string this filter can
|
||||
be used. It will automatically format based on the language used by the
|
||||
current context which is based on the locale of the current alt.
|
||||
|
||||
A different language can be provided with the `locale` parameter.
|
||||
|
||||
The following formats are locale specific:
|
||||
|
||||
| Format | How it Looks
|
||||
| -------- | ----------------
|
||||
| `full` | Wednesday, January 13, 2016 at 7:08:35 AM GMT+00:00
|
||||
| `long` | January 13, 2016 at 7:08:35 AM +0000
|
||||
| `medium` | Jan 13, 2016, 7:08:35 AM
|
||||
| `short` | 1/13/16, 7:08 AM
|
||||
|
||||
In addition to that format codes from the CLDR project can be used. For
|
||||
more information see [CLDR Date/Time Symbols
|
||||
:ext](http://cldr.unicode.org/translation/date-time).
|
||||
|
||||
## Examples
|
||||
|
||||
```html+jinja
|
||||
<p>Date: {{ this.pub_date|datetimeformat('full') }}
|
||||
```
|
||||
|
||||
Or with custom CLDR format strings:
|
||||
|
||||
```html+jinja
|
||||
<p>Date: {{ this.pub_date|datetimeformat('yyyyy.MMMM.dd GGG hh:mm a') }}
|
||||
```
|
|
@ -0,0 +1,22 @@
|
|||
title: markdown
|
||||
---
|
||||
signature: source
|
||||
---
|
||||
summary: Converts markdown to HTML
|
||||
---
|
||||
type: filter
|
||||
---
|
||||
version_added: 2.0
|
||||
---
|
||||
body:
|
||||
|
||||
This filter converts a markdown string into HTML. This behaves the same as
|
||||
the [Markdown Type :ref](../../../db/types/markdown/).
|
||||
|
||||
## Example
|
||||
|
||||
```html+jinja
|
||||
<div class="example">
|
||||
{{ "Hello **World**"|markdown }}
|
||||
</div>
|
||||
```
|
|
@ -0,0 +1,32 @@
|
|||
title: build_url
|
||||
---
|
||||
module: lektor.utils
|
||||
---
|
||||
signature: pieces, trailing_slash=None
|
||||
---
|
||||
summary: Helper function that assists in building URL paths.
|
||||
---
|
||||
type: function
|
||||
---
|
||||
version_added: 2.0
|
||||
---
|
||||
body:
|
||||
|
||||
This function assists in creating URL paths from individual segments. This
|
||||
is particularly useful for building virtual source objects. It takes a bunch
|
||||
of path segments and returns an absolute path. The default behavior is to
|
||||
guess the trailing slash based on the presence of a dot in the last path
|
||||
segment. If you want to override the detection you can explicitly pass
|
||||
`True` to enforce a trailing slash or `False` to avoid it.
|
||||
|
||||
## Example
|
||||
|
||||
```pycon
|
||||
>>> from lektor.utils import build_url
|
||||
>>> build_url(['foo', 42])
|
||||
u'/foo/42/'
|
||||
>>> build_url(['foo', None, 23])
|
||||
u'/foo/23/'
|
||||
>>> build_url(['foo', 'hello.html'])
|
||||
u'/foo/hello.html'
|
||||
```
|
|
@ -0,0 +1,45 @@
|
|||
title: join_path
|
||||
---
|
||||
module: lektor.utils
|
||||
---
|
||||
signature: a, b
|
||||
---
|
||||
summary: Joins two Lektor paths together correctly.
|
||||
---
|
||||
template_var:
|
||||
---
|
||||
type: function
|
||||
---
|
||||
version_added: 2.0
|
||||
---
|
||||
body:
|
||||
|
||||
Given two Lektor paths this joins them together with the rules that Lektor
|
||||
set up for this. In particular this is important in the presence of
|
||||
virtual paths which have their own join semantics.
|
||||
|
||||
## Example
|
||||
|
||||
```pycon
|
||||
>>> from lektor.utils import join_path
|
||||
>>> join_path('/blog', 'hello-world')
|
||||
'/blog/hello-world'
|
||||
>>> join_path('/blog', '@archive')
|
||||
'/blog@archive'
|
||||
>>> join_path('/blog@archive', '2015')
|
||||
'/blog@archive/2015'
|
||||
>>> join_path('/blog@archive/2015', '..')
|
||||
'/blog@archive'
|
||||
>>> join_path('/blog@archive', '..')
|
||||
'/blog'
|
||||
```
|
||||
|
||||
Special note should be taken for the numeric virtual path of paginations. It
|
||||
is considered to be on the same level as the actual record:
|
||||
|
||||
```pycon
|
||||
>>> join_path('/blog@2', '..')
|
||||
'/'
|
||||
>>> join_path('/blog@2', '.')
|
||||
'/blog@2'
|
||||
```
|
|
@ -0,0 +1,25 @@
|
|||
title: parse_path
|
||||
---
|
||||
module: lektor.utils
|
||||
---
|
||||
summary: Parses a path into components.
|
||||
---
|
||||
type: function
|
||||
---
|
||||
version_added: 2.0
|
||||
---
|
||||
body:
|
||||
|
||||
This function parses a path into the individual components it's made from.
|
||||
The path is always assumed to be absolute and made absolute if it's not yet
|
||||
so. The root path is the empty list.
|
||||
|
||||
## Example
|
||||
|
||||
```pycon
|
||||
>>> from lektor.utils import parse_path
|
||||
>>> parse_path('/foo/bar')
|
||||
['foo', 'bar']
|
||||
>>> parse_path('/')
|
||||
[]
|
||||
```
|
|
@ -0,0 +1,45 @@
|
|||
title: process_image
|
||||
---
|
||||
module: lektor.imagetools
|
||||
---
|
||||
signature: ctx, source_image, dst_filename, width, height=None
|
||||
---
|
||||
summary: Build an image from a source image, optionally compressing and resizing.
|
||||
---
|
||||
type: function
|
||||
---
|
||||
version_added: 2.0
|
||||
---
|
||||
body:
|
||||
|
||||
This function takes a [Context :ref](../../build/context/) object, the absolute paths to the image's source and target files, and the target image's width. If height is `None`, it is calculated from the source image width and the target width so that the image is scaled proportionally.
|
||||
|
||||
Used internally for the implementation of [thumbnail :ref](../../db/record/thumbnail), and exposed as an API for image-processing plugins.
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from lektor.build_programs import AttachmentBuildProgram
|
||||
from lektor.context import get_ctx
|
||||
from lektor.db import Image
|
||||
from lektor.imagetools import process_image
|
||||
from lektor.pluginsystem import Plugin
|
||||
|
||||
|
||||
class ResizeBuildProgram(AttachmentBuildProgram):
|
||||
def build_artifact(self, artifact):
|
||||
ctx = get_ctx()
|
||||
width = 1024
|
||||
source_img = artifact.source_obj.attachment_filename
|
||||
artifact.ensure_dir()
|
||||
|
||||
process_image(ctx,
|
||||
source_img,
|
||||
artifact.dst_filename,
|
||||
width)
|
||||
|
||||
|
||||
class ImageResizePlugin(Plugin):
|
||||
def on_setup_env(self, **extra):
|
||||
self.env.add_build_program(Image, ResizeBuildProgram)
|
||||
```
|
|
@ -9,6 +9,12 @@ to a content file. This allows you to translate your website into many
|
|||
different languages or to customize content specifically for some languages
|
||||
or regions.
|
||||
|
||||
!!!! Alternatives are a fully functional feature in Lektor but very
|
||||
underdocumented and lacking in parts. The result of this is that it might not
|
||||
be working exactly as you expect in parts. In particular one of the limiting
|
||||
factors is that you need to have at least one alternative at the URL root or
|
||||
the system will refuse to build the website.
|
||||
|
||||
## Enabling Alternatives
|
||||
|
||||
To enable alternatives you need to extend your [Project File
|
||||
|
@ -66,3 +72,11 @@ targeted, different files will be used. This table visualizes this:
|
|||
| | ✓ | | fr | contents+fr.lr
|
||||
| | | ✓ | en | contents+en.lr
|
||||
| | | ✓ | fr | *missing*
|
||||
|
||||
## Alternatives and Paths
|
||||
|
||||
Alternatives have a special behavior with regards to paths. They alternative
|
||||
code does not exist in the path! This can be confusing at first, but has the
|
||||
advantage that they automatically work in most places as the paths are the
|
||||
same for differnent alternatives. For more information see
|
||||
[Alternatives and Paths :ref](../paths/#alternatives-and-paths).
|
||||
|
|
|
@ -49,7 +49,7 @@ Each page is associated with a model and a template. Each page needs to have
|
|||
a model that defines which fields exist. The template by default matches the
|
||||
model name but it can be overridden on a per-page basis.
|
||||
|
||||
So how is the model selected? Either expicitly in the `contents.lr` file
|
||||
So how is the model selected? Either explicitly in the `contents.lr` file
|
||||
with the `_model` key or by configuration and convention. Lektor will
|
||||
select the default model based on trying things in this order:
|
||||
|
||||
|
@ -58,7 +58,7 @@ select the default model based on trying things in this order:
|
|||
3. model matching the page ID
|
||||
4. the model named `page`
|
||||
|
||||
The template is always the name of the model with `.html` as extension. If
|
||||
The template is always the name of the model with `.html` as extension. It
|
||||
can be overridden in the content file with the `_template` field.
|
||||
|
||||
## Content File Format
|
||||
|
@ -80,11 +80,15 @@ The page body goes here
|
|||
|
||||
Fields are separated by three dashes `---` and follow the format `key: value`.
|
||||
For values with multiple lines it's recommended to insert two newlines after
|
||||
the key. To format of each field is specific to how the model is configured.
|
||||
the key. The format of each field is specific to how the model is configured.
|
||||
|
||||
Some fields are plain text, others can be markdown syntax and more. These
|
||||
fields become available for rendering in the template automatically.
|
||||
|
||||
**Tip:** If you want to use `---` itself in the document text, just add another
|
||||
!!! If you want to use `---` itself in the document text, just add another
|
||||
dash. This means `----` will render as `---` and `-----` will render as
|
||||
`----` etc.
|
||||
|
||||
Fields prefixed with an underscore are so-called system fields. They are
|
||||
provided by Lektor and customize behavior within Lektor. For a list of
|
||||
available fields see [System Fields :ref](../api/db/system-fields/).
|
||||
|
|
|
@ -15,7 +15,7 @@ This allows you to build complex pages made from individual components.
|
|||
|
||||
When you add a `flow` field to one of your models you essentially gain the
|
||||
ability to attach as many blocks as you want into this field. Each block
|
||||
corresponds to a specific [Flow Block Models :ref](../../models/flow/) and
|
||||
corresponds to a specific [Flow Block Model :ref](../../models/flow/) and
|
||||
can render a separate template.
|
||||
|
||||
This is a very powerful feature as it allows you to be very flexible in the
|
||||
|
|
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 22 KiB |
|
@ -0,0 +1,96 @@
|
|||
title: Paths
|
||||
---
|
||||
summary: An explanation about how paths in Lektor work.
|
||||
---
|
||||
body:
|
||||
|
||||
!! This refers mostly to Lektor 2.0. If you are using an older version of
|
||||
Lektor the virtual path feature is not implemented yet and a lot of this just
|
||||
does not apply.
|
||||
|
||||
Lektor attempts to map the paths from the content folder as closely as possible
|
||||
to the final URLs (at least in the default configuration). There are various
|
||||
ways in which this can be customized (see [URLs and Slugs :ref](../urls/)) and
|
||||
there are situations in which Lektor needs to render out content that does not
|
||||
actually directly correspond to a path on the file system.
|
||||
|
||||
Here we try to explain how the path system in Lektor works and what it means.
|
||||
|
||||
## What is a Path
|
||||
|
||||
A path in Lektor is a string that uniquely identifies a [Source Object
|
||||
:ref](../../api/db/obj/). For the most part these directly point to
|
||||
`content.lr` files or attachments therein. These paths always use forward
|
||||
slashes, no matter on which platform Lektor runs. So for instance if you
|
||||
have a file named `projects/my-project/contents.lr` then the path for this
|
||||
record will be `/projects/my-project`. Thereby it does not matter if the
|
||||
slug or final URL was changed, the path is always the same.
|
||||
|
||||
Lektor uses paths to refer to records by default in all cases. This means that
|
||||
if you use the [url filter :ref](../../api/templates/filters/url/) for instance
|
||||
it will operate on paths and not URLs!
|
||||
|
||||
But what about sources that do not directly correspond to something on the
|
||||
file system? This is where virtual paths come in. Virtual paths are
|
||||
separated from physical paths with an at (`@`) sign. Virtual paths are always
|
||||
attached to a record and point to things that are not actually coming from the
|
||||
content folder but something else.
|
||||
|
||||
Virtual paths are for instance used to refer to paginated pages or to some
|
||||
resources that plugins add.
|
||||
|
||||
## Virtual Paths
|
||||
|
||||
Virtual paths are added behind physical paths and are separated by an at
|
||||
sign (`@`). The only virtual path that is supported by Lektor out of the box
|
||||
is the special numeric virtual path which can be used to refer to specific
|
||||
pages for a pagination. For instance `/blog@1` refers to the first page of
|
||||
the `blog` page, `/blog@2` to the second etc. There are however plugins that
|
||||
add virtual paths to refer to their own resources. For instance a plugin can
|
||||
register `/blog@feed` to refer to a RSS/Atom feed for the blog.
|
||||
|
||||
## Relative Paths
|
||||
|
||||
Now that we talked a bit about paths, we should probably cover how relative
|
||||
paths work. Relative paths work similar to how you expect them to work on
|
||||
most operating systems but they can operate on the virtual as well as the
|
||||
physical path. There is also some special behavior with regards to the numeric
|
||||
virtual path for pagination.
|
||||
|
||||
For the most part `.` refers to the same page and `..` refers to the page one
|
||||
level up. If you use a path that just contains of a virtual path, then it's
|
||||
attached to the current page to replace the active virtual path.
|
||||
|
||||
| Current | Relative | Result
|
||||
| --------------- | ------------ | ----------------
|
||||
| `/blog` | `..` | `/`
|
||||
| `/blog` | `.` | `/blog`
|
||||
| `/blog/post` | `..` | `/blog`
|
||||
| `/blog` | `@2` | `/blog@2`
|
||||
| `/blog@2` | `@1` | `/blog@1`
|
||||
| `/blog@feed` | `recent` | `/blog@feed/recent`
|
||||
| `/blog@feed` | `..` | `/blog@feed`
|
||||
|
||||
However! The numeric path is special with regards because it's considered to
|
||||
belong to the current page:
|
||||
|
||||
| Current | Relative | Result
|
||||
| --------------- | ------------ | ----------------
|
||||
| `/blog@2` | `.` | `/blog@2`
|
||||
| `/blog@2` | `..` | `/`
|
||||
|
||||
## Alternatives and Paths
|
||||
|
||||
If you have used Lektor a bit you might be wondering how [Alternatives
|
||||
:ref](../alts/) work with paths. The answer might be surprising but it's
|
||||
basically that they don't really work with paths. Alternatives are
|
||||
implemented on a level higher than paths. If you have a page that exists
|
||||
in both German and English alternative then they will have the same path.
|
||||
The alternative code is supplied separately.
|
||||
|
||||
This is done so that you can later start introducing alternatives to sites
|
||||
that were never aware of them without having to go everywhere and start
|
||||
passing alternative information around. While there are indeed some places
|
||||
where you might have to perform some changes (especially if you perform
|
||||
manual queries in the templates) for the most part adding alternatives to an
|
||||
existing site later is a trivial matter.
|
|
@ -0,0 +1,61 @@
|
|||
title: System Fields
|
||||
---
|
||||
summary: Brief overview of the system fields in Lektor.
|
||||
---
|
||||
body:
|
||||
|
||||
In addition to the fields that are defined through the [Data Model
|
||||
:ref](../../models/) there are a lot of fields that come directly from Lektor.
|
||||
They can be easily recognized because they are prefixed by a leading underscore
|
||||
(`_`).
|
||||
|
||||
These fields influence how Lektor treats pages and attachment. Here we will
|
||||
just go over the most important ones but for a full list you can have a look
|
||||
[at the API Documentation :ref](../../api/db/system-fields/).
|
||||
|
||||
## `_hidden`
|
||||
|
||||
This field is a boolean value and controls if a page is hidden or not. If a
|
||||
page is hidden all of it's children will automatically also be hidden. A page
|
||||
that is hidden will not be processed by Lektor's build system. This means such
|
||||
pages can be discovered through the query API but they are not rendered
|
||||
themselves. This is useful for situations where you want to have information
|
||||
stored but not rendered. This is for instance used to [implement Single-Page
|
||||
websites :ref](../../guides/single-page/).
|
||||
|
||||
*To read more read [the field documentation :ref](../../api/db/system-fields/hidden/).*
|
||||
|
||||
## `_discoverable`
|
||||
|
||||
This fields in many ways is similar to `_hidden` but instead makes a page
|
||||
be only hidden from queries. In particular this means that it will still be
|
||||
processed and built, but that it becomes harder for code to reference it.
|
||||
Pages that are not set to discoverable can only be found through queries if
|
||||
they are [explicitly included :ref](../../api/db/query/include-undiscoverable/).
|
||||
|
||||
!! This field was added in Lektor 2.0. In earlier versions this feature is
|
||||
unavailable.
|
||||
|
||||
*To read more read [the field documentation :ref](../../api/db/system-fields/discoverable/).*
|
||||
|
||||
## `_model`
|
||||
|
||||
This key sets the [Data Model :ref](../../models/) that is used for the record.
|
||||
If not set this defaults to one of different choices depending on configuration
|
||||
or name. Parent models can pre-define models for children which is the used if
|
||||
this key is not set. If not set this will be picked up by an algorithm.
|
||||
See [Default Model Selection :ref](../../models/selection/) for more information.
|
||||
|
||||
*To read more read [the field documentation :ref](../../api/db/system-fields/model/).*
|
||||
|
||||
## `_slug`
|
||||
|
||||
This field can override the default URL slug. For more information about this
|
||||
feature see [URLs and Slugs :ref](../urls/).
|
||||
|
||||
## `_template`
|
||||
|
||||
This field can be used to override the default template selection which is
|
||||
just the model name with the `.html` extension.
|
||||
|
||||
*To read more read [the field documentation :ref](../../api/db/system-fields/template/).*
|
|
@ -26,7 +26,7 @@ successfully*.
|
|||
## The Build Pipeline
|
||||
|
||||
So let's cover the building first. When you use the Lektor server locally,
|
||||
Lektor constantly builds our your website into static HTML files behind the
|
||||
Lektor constantly builds out your website into static HTML files behind the
|
||||
scenes into the default build folder. This folder is in an operating system
|
||||
specific location. If you want to know where that folder is, you can use this
|
||||
command:
|
||||
|
@ -55,7 +55,7 @@ potential deployment target add a `[servers.NAME]` section. The supported
|
|||
keys are `name` for an optional human readable name of the server, `enabled` to
|
||||
enable or disable it (defaults to `true`) and `target` which is the URL to
|
||||
publish to. Additionally one of the servers can have `default` set to `yes`
|
||||
to set it as default. Here an example:
|
||||
to set it as default. Here is an example:
|
||||
|
||||
```ini
|
||||
[servers.production]
|
||||
|
@ -93,14 +93,18 @@ the `LEKTOR_OUTPUT_PATH` environment variable.
|
|||
the credentials directly in the URL. If you do this, please ensure that
|
||||
you keep your project file secure as loss of the project file can mean that
|
||||
people get access to your server. Alternatively you can also provide username
|
||||
and password through the command line of environment variables.
|
||||
and password through the command line or environment variables.
|
||||
|
||||
The following targets are supported for the `target` folder:
|
||||
The following targets are supported for the `target` field:
|
||||
|
||||
* [rsync](rsync/)
|
||||
* [FTP](ftp/)
|
||||
* [GitHub Pages](ghpages/)
|
||||
|
||||
In addition, third-party plugins are available for these targets:
|
||||
|
||||
* [S3 :ext](https://github.com/spenczar/lektor-s3)
|
||||
|
||||
## Manual Deployments
|
||||
|
||||
If you want to manually deploy something through the favorite tool of yours
|
||||
|
|
|
@ -52,7 +52,7 @@ duplicate entries in that file are resolved.
|
|||
## Using Other Tools
|
||||
|
||||
The FTP support in Lektor is quite rudimentary and in some situations you
|
||||
might want to use a different tool like filezilla to do your synchronziations
|
||||
might want to use a different tool like filezilla to do your synchronizations
|
||||
instead. In that case you just need to point your FTP client to the build
|
||||
folder. To find out where this is, just run this command:
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ body:
|
|||
* `ghpages+https://username/repository`
|
||||
|
||||
A popular way to host websites for Open Source projects is the GitHub pages.
|
||||
It's a free services provided by [GitHub :ext](http://github.com/) which allows
|
||||
It's a free service provided by [GitHub :ext](http://github.com/) which allows
|
||||
to host completely static websites through GitHub.
|
||||
|
||||
The way this is implemented in Lektor currently is that Lektor will force-push
|
||||
|
|
|
@ -57,14 +57,14 @@ configure the access credentials. We will get to that.
|
|||
|
||||
## Enabling Travis
|
||||
|
||||
So now that we have all that configured we need to tellt travis to build the
|
||||
So now that we have all that configured we need to tell travis to build the
|
||||
repository. For that just head to your [Travis-CI Profile
|
||||
:ext](https://travis-ci.org/profile) and enable the repository. If it does not
|
||||
show up yet, you can force a sync with the click of a button.
|
||||
|
||||
## Access Credentials
|
||||
|
||||
So how do you savely provide your credentials? Lektor accepts username and
|
||||
So how do you safely provide your credentials? Lektor accepts username and
|
||||
password for the `ghpages+https` transport via the `LEKTOR_DEPLOY_USERNAME`
|
||||
and `LEKTOR_DEPLOY_PASSWORD` environment variables. These can be set in the
|
||||
Travis-CI settings of your repository on travis-ci.org in secret so they are
|
||||
|
@ -87,6 +87,12 @@ To solve this problem we recommend two things:
|
|||
Once you have done that travis will start deploying the website on every
|
||||
commit.
|
||||
|
||||
!!!! When copy/pasting username and password into travis please ensure that
|
||||
you do not copy any leading or trailing whitespace with it. This will not
|
||||
just break the build but also reveal the password in the process. For
|
||||
more information see [travis-ci#4139
|
||||
:ext](https://github.com/travis-ci/travis-ci/issues/4139).
|
||||
|
||||
## Committer Information
|
||||
|
||||
By default the commits to the `gh-pages` branch will be authored by a user
|
||||
|
@ -118,3 +124,22 @@ cache:
|
|||
install: "pip install Lektor"
|
||||
script: "lektor build && lektor deploy ghpages"
|
||||
```
|
||||
|
||||
## Restricting Branches
|
||||
|
||||
If you plan on having different branches and contributors you should disable
|
||||
the deployment to the master branch only. You can do this with the following
|
||||
config:
|
||||
|
||||
```yaml
|
||||
language: python
|
||||
python: 2.7
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.cache/pip
|
||||
- $HOME/.cache/lektor/builds
|
||||
install: "pip install Lektor"
|
||||
script:
|
||||
- "lektor build"
|
||||
- 'test "$TRAVIS_PULL_REQUEST" == false && test "$TRAVIS_BRANCH" == master && lektor deploy ghpages'
|
||||
```
|
||||
|
|
|
@ -93,6 +93,7 @@ page.
|
|||
{% extends "layout.html" %}
|
||||
{% from "macros/pagination.html" import render_pagination %}
|
||||
{% block title %}My Blog{% endblock %}
|
||||
{% block body %}
|
||||
<h1>My Blog</h1>
|
||||
|
||||
<ul class="blog-index">
|
||||
|
@ -122,7 +123,7 @@ do:
|
|||
{% extends "layout.html" %}
|
||||
{% block title %}{{ this.title }} | My Blog{% endblock %}
|
||||
{% block body %}
|
||||
<1h>{{ this.title }}
|
||||
<h1>{{ this.title }}
|
||||
<p class="meta">
|
||||
by {{ this.author }}
|
||||
on {{ this.pub_date|dateformat('full') }}
|
||||
|
@ -141,7 +142,7 @@ with the name of your blog. For instance just `content/blog` and put a
|
|||
_model: blog
|
||||
```
|
||||
|
||||
Now you can head to the admin to create new blog posts.
|
||||
Now you can head to the admin UI to create new blog posts.
|
||||
|
||||
## Changing the URL Structure
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ source = site.query('/project-categories')
|
|||
```
|
||||
|
||||
The above models should be mostly clear. What is probably not entirely clear
|
||||
is the `source` parameter for the categories. Esentially we instruct the
|
||||
is the `source` parameter for the categories. Essentially we instruct the
|
||||
admin panel to fill the selection for the checkboxes from the child pages
|
||||
below the `project-categories` folder. This is where we will maintain the
|
||||
categories.
|
||||
|
@ -180,7 +180,8 @@ markup that appears on multiple pages.
|
|||
|
||||
```html+jinja
|
||||
{% extends "layout.html" %}
|
||||
{% from "macros/projects.html" import render_category_nav %}
|
||||
{% from "macros/projects.html" import render_category_nav,
|
||||
render_project_list %}
|
||||
{% block title %}Project Category {{ this.name }}{% endblock %}
|
||||
{% block body %}
|
||||
<h1>Project Category {{ this.name }}</h1>
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
title: Error Pages
|
||||
---
|
||||
summary: Brief introduction into error pages in Lektor.
|
||||
---
|
||||
body:
|
||||
|
||||
Because Lektor renders out static pages the question comes up what happens if a
|
||||
page cannot be found. This is typically achieved by a special page that a
|
||||
server will then use as a stand-in for a page that otherwise cannot be found.
|
||||
|
||||
In Lektor as a convention this page should be called `404.html`. While in
|
||||
reality the name of this page largely depends on how you deploy your pages we
|
||||
are sticking with the generally accepted location of calling it `404.html`.
|
||||
This will work on GitHub Pages and some other environments where this cannot
|
||||
be otherwise configured and most web servers can be configured to use this file
|
||||
for URLs that are not found.
|
||||
|
||||
! In versions of Lektor before 2.0 custom 404 pages will not be honored by the
|
||||
development server. To test those you will need to explicitly navigate to
|
||||
`/404.html`.
|
||||
|
||||
## URLs on 404 Pages
|
||||
|
||||
If you are using 404 pages then these pages can appear at any URL. This means
|
||||
that relative URLs *will not work*. If you want to use custom error pages you
|
||||
will have to set the `url_style` to `absolute` as otherwise URLs on an error
|
||||
page will not work. Just add this to your project file:
|
||||
|
||||
```ini
|
||||
[project]
|
||||
url_style = absolute
|
||||
```
|
||||
|
||||
For more information about this you can read the [Project File Documentation
|
||||
:ref](../../project/file/).
|
||||
|
||||
## Creating an Error Page
|
||||
|
||||
You can create easy create a 404 page by creating a `404.html/contents.lr`
|
||||
file. If you do not care much about the contents and structure of the file
|
||||
you can just point it to an empty model (`none`) and manually select a
|
||||
`404.html` template like this:
|
||||
|
||||
```
|
||||
_model: none
|
||||
----
|
||||
_template: 404.html
|
||||
```
|
||||
|
||||
Then just create a `404.html` template with the intended contents.
|
||||
|
||||
## Server Configuration
|
||||
|
||||
If you are deploying such pages to your own servers you will need to ensure
|
||||
that the error pages are activated. Depending on the server used this will
|
||||
work slightly differently.
|
||||
|
||||
### Apache
|
||||
|
||||
Making custom error pages work is easiest with Apache. If `.htaccess` files
|
||||
are enabled you can just put a file with that name into your `assets` folder
|
||||
and add the following line to it:
|
||||
|
||||
```apache
|
||||
ErrorDocument 404 /404.html
|
||||
```
|
||||
|
||||
Alternatively you can add the above line into a `VirtualHost` or `Directory`
|
||||
section in your main config file.
|
||||
|
||||
### nginx
|
||||
|
||||
For nginx you need to enable the error document in your main config file. Just
|
||||
add it to your `server` section:
|
||||
|
||||
```nginx
|
||||
error_page 404 /404.html;
|
||||
```
|
||||
|
||||
### lighttpd
|
||||
|
||||
If you are using lighttpd you can configure an error page for 404 this way:
|
||||
|
||||
```ini
|
||||
server.error-handler-404 = "/404.html"
|
||||
```
|