Merge remote-tracking branch 'refs/remotes/origin/master' into feature/install-windows
|
@ -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
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
Most content in this repository is licensed under the CC0
|
||||
license (https://creativecommons.org/publicdomain/zero/1.0/legalcode).
|
||||
|
||||
It applies to content below:
|
||||
|
||||
* content/
|
||||
* databags/
|
||||
* models/
|
||||
* flowblocks/
|
||||
* templates/
|
||||
* configs/
|
||||
|
||||
It does not apply to the assets and design. For the packages used individual
|
||||
licenses apply.
|
|
@ -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
|
||||
|
@ -24,6 +28,7 @@ if 1:
|
|||
import json
|
||||
import urllib
|
||||
import tempfile
|
||||
import shutil
|
||||
from subprocess import Popen
|
||||
|
||||
sys.stdin = open('/dev/tty', 'r')
|
||||
|
@ -59,6 +64,32 @@ if 1:
|
|||
os.path.dirname(path), 'lib', 'lektor')
|
||||
None, None
|
||||
|
||||
def get_confirmation():
|
||||
while 1:
|
||||
input = raw_input('Continue? [Yn] ').lower().strip()
|
||||
if input in ('', 'y'):
|
||||
break
|
||||
elif input == 'n':
|
||||
print
|
||||
print 'Aborted!'
|
||||
sys.exit()
|
||||
|
||||
def wipe_installation(lib_dir, symlink_path):
|
||||
if os.path.lexists(symlink_path):
|
||||
os.remove(symlink_path)
|
||||
if os.path.exists(lib_dir):
|
||||
shutil.rmtree(lib_dir, ignore_errors=True)
|
||||
|
||||
def check_installation(lib_dir, bin_dir):
|
||||
symlink_path = os.path.join(bin_dir, 'lektor')
|
||||
if os.path.exists(lib_dir) or os.path.lexists(symlink_path):
|
||||
print ' Lektor seems to be installed already.'
|
||||
print ' Continuing will wipe %s and remove %s' % (lib_dir, symlink_path)
|
||||
print
|
||||
get_confirmation()
|
||||
print
|
||||
wipe_installation(lib_dir, symlink_path)
|
||||
|
||||
def fail(message):
|
||||
print 'Error: %s' % message
|
||||
sys.exit(1)
|
||||
|
@ -67,17 +98,19 @@ if 1:
|
|||
t = tempfile.mkdtemp()
|
||||
Popen('curl -sf "%s" | tar -xzf - --strip-components=1' %
|
||||
virtualenv_url, shell=True, cwd=t).wait()
|
||||
|
||||
try:
|
||||
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'),
|
||||
os.path.join(bin_dir, 'lektor'))
|
||||
|
||||
def main():
|
||||
print
|
||||
print 'Welcome to Lektor'
|
||||
print
|
||||
print 'This script will install Lektor on your computer.'
|
||||
|
@ -92,17 +125,14 @@ if 1:
|
|||
if bin_dir is None or lib_dir is None:
|
||||
fail('Could not determine installation location for Lektor.')
|
||||
|
||||
check_installation(lib_dir, bin_dir)
|
||||
|
||||
print 'Installing at:'
|
||||
print ' bin: %s' % bin_dir
|
||||
print ' app: %s' % lib_dir
|
||||
print
|
||||
|
||||
while 1:
|
||||
input = raw_input('Continue? [Yn] ').lower().strip()
|
||||
if input in ('', 'y'):
|
||||
break
|
||||
elif input == 'n':
|
||||
print 'Aborted!'
|
||||
sys.exit()
|
||||
get_confirmation()
|
||||
|
||||
for url in json.load(urllib.urlopen(VENV_URL))['urls']:
|
||||
if url['python_version'] == 'source':
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"version":3,"file":"app.js","sources":["webpack:///app.js","webpack:///"],"mappings":"AAAA;;;;;;;;;;;;;ACwLA;;;;;;;;;;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 |
|
@ -43,14 +43,14 @@ There the hacker's favorite projects like
|
|||
[Pelican](https://github.com/getpelican/pelican) or
|
||||
[Jekyll](https://jekyllrb.com/) which support generating websites out of static
|
||||
files that are tracked through version control and there are CMS systems like
|
||||
[Statamic](http://statamic.com/) which store all of it's data in flat files
|
||||
[Statamic](http://statamic.com/) which store all of its data in flat files
|
||||
— but as mentioned — needs PHP.
|
||||
|
||||
None of those were what I was looking for. Static file generators like Jekyll
|
||||
are nice in a way but not just end user unfriendly, but also very limited in
|
||||
what you can do with them. They are more suited for building blogs than
|
||||
more complex sites. I have tried a bunch of them and build different things
|
||||
with them, but ultimately always felt like somethign is missing.
|
||||
more complex sites. I tried a bunch of them and built different things
|
||||
with them, but ultimately always felt like something was missing.
|
||||
|
||||
After about two years of frustration with that situation I finally sat down
|
||||
and spend some time working on a system to solve this problem. May I
|
||||
|
@ -59,7 +59,7 @@ introduce: Lektor.
|
|||
## What is Lektor?
|
||||
|
||||
Lektor combines the experience of using a content management system like
|
||||
Workdpress with a static website generator like Jekyll and has some of the
|
||||
Wordpress with a static website generator like Jekyll and has some of the
|
||||
flexibility of a web development framework like Django. It can run locally
|
||||
on your computer and deploy to remote servers. All source assets are stored
|
||||
either in version control or Dropbox and when you are satisfied with the end
|
||||
|
@ -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,17 +16,25 @@ 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">
|
||||
</div>
|
||||
</div>
|
||||
<div class="install-row hide-for-windows">
|
||||
<pre>curl -sf https://www.getlektor.com/install.sh | sh</pre>
|
||||
</div>
|
||||
</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>
|
||||
|
@ -37,22 +43,20 @@ contents:
|
|||
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>
|
||||
</div>
|
||||
<div class="install-row">
|
||||
<pre>curl -sf https://www.getlektor.com/install.sh | sh</pre>
|
||||
</div>
|
||||
</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 ####
|
||||
|
@ -117,7 +121,7 @@ text:
|
|||
|
||||
## Open Source
|
||||
|
||||
Lektor at it's core is a three clause [BSD licensed
|
||||
Lektor at its core is a three clause [BSD licensed
|
||||
](https://raw.githubusercontent.com/lektor/lektor/master/LICENSE) Open Source
|
||||
project written in Python and Node.js.
|
||||
----
|
||||
|
|
|
@ -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/).
|
|
@ -16,15 +16,23 @@ 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.
|
||||
| `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
|
||||
|
||||
|
@ -59,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') }}
|
||||
```
|
|
@ -20,7 +20,7 @@ For configuration you can define which blocks are allowed by setting the
|
|||
that are allowed. If not defined, all flow blocks become available.
|
||||
|
||||
The text format for flow blocks in the `contents.lr` file looks a bit more
|
||||
complex because of it's nested nature, but in essence it's this:
|
||||
complex because of its nested nature, but in essence it's this:
|
||||
|
||||
```
|
||||
#### name-of-flow-block ####
|
||||
|
@ -74,5 +74,5 @@ or more complex:
|
|||
```
|
||||
|
||||
To see how the actual blocks are rendered have a look at the main
|
||||
[Flow Documentation :ref](../../content/flow/) which covers templating
|
||||
[Flow Documentation :ref](../../../../content/flow/) which covers templating
|
||||
in detail.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -9,7 +9,7 @@ summary: Provides access to a project file.
|
|||
body:
|
||||
|
||||
The project class is one of the most basic classes that Lektor uses to
|
||||
implement it's building process. It's generated very early on when the
|
||||
implement its building process. It's generated very early on when the
|
||||
application needs to interact with the project file on disk. This
|
||||
class is mostly useful for building scripts that use the Lektor API and
|
||||
not so much in other situations as it's not directly playing a role in
|
||||
|
|
|
@ -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>
|
||||
```
|
|
@ -4,7 +4,7 @@ type: filter
|
|||
---
|
||||
summary: Generates a relative URL from the current page to another.
|
||||
---
|
||||
signature: alt=None
|
||||
signature: alt=None, absolute=None, external=None
|
||||
---
|
||||
body:
|
||||
|
||||
|
|
|
@ -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)
|
||||
```
|
|
@ -2,7 +2,7 @@ title: Command Line
|
|||
---
|
||||
sort_key: 900
|
||||
---
|
||||
summary: Covers Lektor's command line interface.
|
||||
summary: Covers Lektor's command line interface behind the lektor executable.
|
||||
---
|
||||
body:
|
||||
|
||||
|
|
|
@ -9,10 +9,16 @@ 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
|
||||
:ref](../../project/). For each alternative a new
|
||||
:ref](../../project/file/). For each alternative a new
|
||||
section has to be added. It's important that one of the alternatives is
|
||||
marked as "primary" which informs the system which of the alternatives is
|
||||
the reference.
|
||||
|
@ -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).
|
||||
|
|
|
@ -9,7 +9,7 @@ directly in the page folder and become available publicly.
|
|||
|
||||
## Attachment Types
|
||||
|
||||
For the most part Lektor does care much about what types your attachments
|
||||
For the most part Lektor does not care much about what types your attachments
|
||||
are but it will specially handle some. In particular image formats supported
|
||||
by browsers have special support for automatic thumbnailing and accessing
|
||||
basic image data.
|
||||
|
@ -33,10 +33,10 @@ extra extension:
|
|||
| sunset.jpeg | sunset.jpeg.lr
|
||||
| code.py | code.py.lr
|
||||
|
||||
Attachments can be [given a default model :ref](../../models/#attachments) or
|
||||
a model can be explicitly given in the content file with the `_model` field.
|
||||
Attachments can be [given a default model :ref](../../models/attachments/) or
|
||||
a model can be explicitly given in the metadata content file with the `_model` field.
|
||||
|
||||
Here a basic example:
|
||||
Here is a basic example:
|
||||
|
||||
```
|
||||
_model: image
|
||||
|
|
|
@ -46,10 +46,10 @@ the rendered end result will be at `/portfolio/project-a/`.
|
|||
## Page, Model and Template
|
||||
|
||||
Each page is associated with a model and a template. Each page needs to have
|
||||
a model that defines with fields exist. The template by default matches the
|
||||
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/).
|
||||
|
|
|
@ -7,7 +7,7 @@ body:
|
|||
Flow is a system in Lektor that allows you to have higher flexibility when
|
||||
creating pages. The [Flow Type :ref](../../api/db/types/flow/) field type
|
||||
that allows you to store multiple different formats of data within the same
|
||||
field each with it's own template.
|
||||
field each with its own template.
|
||||
|
||||
This allows you to build complex pages made from individual components.
|
||||
|
||||
|
@ -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/).*
|
|
@ -15,15 +15,15 @@ URL which can be customized. They are roughly speaking something like a
|
|||
file name. For instance `foo-bar` can be a slug in `/foo-bar/`. The default
|
||||
slug of a page is the ID of the page. So if you have a page called
|
||||
`/foo/bar/contents.lr` then the default slug is `bar`. As you can see the
|
||||
full URL is comprised of it's own slug concatenated with all the slugs of
|
||||
full URL is comprised of its own slug concatenated with all the slugs of
|
||||
all parents.
|
||||
|
||||
Can a slug contain slashes? Yes indeed it can. A slug is free to contain
|
||||
any slashes if it wants and they will be handled just as you expect. So it's
|
||||
perfectly valid for a page to have `2015/5/demo` as slug. What's not possible
|
||||
is for a page to pretent that it belongs to a different parent. The parent
|
||||
paths are always added to it. So once the page is below `/foo` the URL path
|
||||
will always begin with the URL path of the page `foo`.
|
||||
is for a page to pretend that it belongs to a different parent. The parent
|
||||
paths are always added to it. So once a page has a parent page `/foo`
|
||||
its URL path will always begin with the URL path of the page `foo`.
|
||||
|
||||
## Slug Customization
|
||||
|
||||
|
@ -44,7 +44,7 @@ The second option is to use the `_slug` system field. This field is available
|
|||
for all models automatically and overrides the slug explicitly. This is
|
||||
particularly useful to force a slug that could not be represented on the file
|
||||
system (for instance because it should contain a slash) or because you want
|
||||
to change the slug for a different [Alternative :ref](../alternatives/). As
|
||||
to change the slug for a different [Alternative :ref](../alts/). As
|
||||
an example a page translated to German might want to translate the slug as well.
|
||||
|
||||
### Implied Slug Configuration
|
||||
|
@ -103,7 +103,7 @@ You can pick the default for URL generation in the project configuration.
|
|||
For more information read about the [Project Configuration
|
||||
:ref](../../project/file/#[project]).
|
||||
|
||||
The default of `relative` a realtive URL style means that you can deploy a
|
||||
The default of `relative`, a relative URL style, means that you can deploy a
|
||||
website to a sub folder without any configuration, however most likely custom
|
||||
404 pages will fail to find the needed assets. Fully canonical URLs are not
|
||||
recommended as default style.
|
||||
|
|