Merge remote-tracking branch 'refs/remotes/origin/master' into feature/install-windows

This commit is contained in:
Elias Zeitfogel 2016-02-10 17:57:56 +01:00
commit e89d92950b
195 changed files with 3119 additions and 689 deletions

View File

@ -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

14
LICENSE Normal file
View File

@ -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.

View File

@ -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

View File

@ -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:

View File

@ -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()
'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':

File diff suppressed because one or more lines are too long

View File

@ -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":""}

File diff suppressed because one or more lines are too long

3
configs/atom.ini Normal file
View File

@ -0,0 +1,3 @@
[transcript]
name = The Transcript
source_path = /blog

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

@ -1 +1,3 @@
_model: blog
---
summary: Lektor's blog

Binary file not shown.

Before

Width:  |  Height:  |  Size: 226 KiB

After

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 279 KiB

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 415 KiB

After

Width:  |  Height:  |  Size: 368 KiB

View File

@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 241 KiB

After

Width:  |  Height:  |  Size: 158 KiB

View File

@ -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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 KiB

View File

@ -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)

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

View File

@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 337 KiB

After

Width:  |  Height:  |  Size: 175 KiB

View File

@ -7,9 +7,7 @@ body:
#### banner ####
image: header.jpg
----
height: 500
----
class: dark
height: 500-tall
----
contents:
@ -18,41 +16,47 @@ contents:
<div class="col-md-8">
<img src="logo.png" alt="Lektor" class="logo">
<p>
a flexible and powerful static content management system for building
A flexible and powerful static content management system for building
complex and beautiful websites out of flat files — for people who do
not want to make a compromise between a CMS and a static blog engine.
<p>
<a href="docs/what/">Read about what makes Lektor different</a>.
Getting your ideas implemented is as easy as making breakfast eggs.
</div>
<div class="col-md-4 visible-md-block visible-lg-block">
<div class="download-btn"><a href="download/"
><i class="glyphicon glyphicon-download-alt"></i> Download</a></div>
<ul class="badges">
<li><a href="https://twitter.com/getlektor"
class="twitter-follow-button" data-show-count="false"
data-size="large" data-dnt="true">Follow @getlektor</a>
<li><a class="github-button" href="https://github.com/lektor/lektor"
data-style="mega" data-count-href="/lektor/lektor/stargazers"
data-count-api="/repos/lektor/lektor#stargazers_count"
data-count-aria-label="# stargazers on GitHub"
aria-label="Star lektor/lektor on GitHub">Star</a>
</ul>
</div>
</div>
<div class="install-row">
<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>
<li><a class="github-button" href="https://github.com/lektor/lektor"
data-style="mega" data-count-href="/lektor/lektor/stargazers"
data-count-api="/repos/lektor/lektor#stargazers_count"
data-count-aria-label="# stargazers on GitHub"
aria-label="Star lektor/lektor on GitHub">Star</a>
<li><a href="https://gitter.im/lektor/lektor"
class="js-gitter-toggle-chat-button"><img
src="https://badges.gitter.im/lektor/lektor.svg"
alt="Join the chat at https://gitter.im/lektor/lektor"></a>
</ul>
</div>
#### text-block ####
text:
## Full of Features
* <i class="feature-circle fa fa-ship"></i> **Deploy Anywhere** Because Lektor
builds out 100% static HTML you can deploy them to any host (including S3,
github pages, any web hoster, etc.)
* <i class="feature-circle fa fa-globe"></i> **Cross Platform** it runs on
github pages, any web host, etc.).
* <i class="feature-circle fa fa-globe"></i> **Cross Platform** It runs on
Linux, OSX and Windows.
* <i class="feature-circle fa fa-file-code-o"></i> **100% File Based** All
source data is well structured and can be tracked in a version control
@ -60,21 +64,21 @@ text:
* <i class="feature-circle fa fa-database"></i> **Flat-File Database** All
source data is stored in a flat-file tree database and can be freely
queried. The layout of that data is completely configurable.
* <i class="feature-circle fa fa-pencil"></i> **Customizable Admin** comes
fully equipped with a flexible and beautiful admin interface so you can
* <i class="feature-circle fa fa-pencil"></i> **Customizable Admin** Lektor
comes fully equipped with a flexible and beautiful admin interface so you can
edit your pages without having to touch the raw sources.
* <i class="feature-circle fa fa-code-fork"></i> **Dependency Tracking**
The build process intelligently tracks dependencies between pages to only
build the files that need rebuilding due to changes.
* <i class="feature-circle fa fa-image"></i> **Image Tools** create
The build process intelligently tracks page dependencies such that it only
rebuilds pages that have changed.
* <i class="feature-circle fa fa-image"></i> **Image Tools** Create
thumbnails and give convenient access to EXIF data.
* <i class="feature-circle fa fa-puzzle-piece"></i> **Plugin System** Lektor
supports loading plugins that can customize a wide range of functionality.
* <i class="feature-circle fa fa-terminal"></i> **Python API** the build
* <i class="feature-circle fa fa-terminal"></i> **Python API** The build
system is written in Python and provides a documented API to extend it and
integrate into other apps.
* <i class="feature-circle fa fa-language"></i> **Multilingual** We can
speak multiple languages and allows you to easily create localized websites.
speak multiple languages and allow you to easily create localized websites.
----
class: two-column-list
#### slideshow ####
@ -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.
----

View File

@ -30,3 +30,5 @@ def build_stylesheet(artifact):
with artifact.open('w') as f:
f.write('Hello World!\n')
```
---
module: lektor.builder

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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
```

View File

@ -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.

View File

@ -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/).

View File

@ -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

View File

@ -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/).

View File

@ -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 %}
```

View File

@ -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.

View File

@ -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 %}
```

View File

@ -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>
```

View File

@ -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>

View File

@ -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 %}
```

View File

@ -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.

View File

@ -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.

View File

@ -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>
```

View File

@ -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/).

View File

@ -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']
```

View File

@ -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/).

View File

@ -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/).

View File

@ -15,16 +15,24 @@ overview page.
The following attributes exist on the pagination object:
| Attribute | Description
| ----------- | ------------
| `current` | The current record
| `prev` | The record for the last page (might be `None`)
| `next` | The record for the net page (might be `None`)
| `total` | The total number of items across all pages
| `pages` | The total number of pages
| `has_prev` | `True` if a previous page exists
| `has_next` | `True` if a next page exists
| `items` | The query that resolves to the children of the current page.
| Attribute | Description
| ------------ | ------------
| `current` | The current record
| `prev` | The record for the last page (might be `None`)
| `next` | The record for the net page (might be `None`)
| `total` | The total number of items across all pages
| `pages` | The total number of pages
| `page` | The number of current page
| `has_prev` | `True` if a previous page exists
| `has_next` | `True` if a next page exists
| `items` | The query that resolves to the children of the current page.
| `for_page()` | Returns the record for a different page.
The `for_page()` function accepts a page number and returns the record for
the other page. For more information also see the virtual path example
below.
!! *Changed in Lektor 2.0:* The `for_page()` method was added.
## Item Query Example
@ -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>
```

View File

@ -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>
```

View File

@ -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.

View File

@ -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.

View File

@ -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).

View File

@ -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.

View File

@ -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>
```

View File

@ -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.

View File

@ -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>
```

View File

@ -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>
```

View File

@ -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 }} -->
```

View File

@ -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/).

View File

@ -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 }} -->
```

View File

@ -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.

View File

@ -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).

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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).

View File

@ -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') }}
```

View File

@ -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.

View File

@ -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
```

View File

@ -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:

View File

@ -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

View File

@ -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)
```

View File

@ -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):

View File

@ -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):

View File

@ -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')
```

View File

@ -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.

View File

@ -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.

View File

@ -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()
```

View File

@ -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
```

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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)
```

View File

@ -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.

View File

@ -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
```

View File

@ -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') }}
```

View File

@ -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>
```

View File

@ -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:

View File

@ -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'
```

View File

@ -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'
```

View File

@ -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('/')
[]
```

View File

@ -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)
```

View File

@ -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:

View File

@ -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).

View File

@ -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

View File

@ -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/).

View File

@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -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.

View File

@ -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/).*

View File

@ -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.

Some files were not shown because too many files have changed in this diff Show More