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

This commit is contained in:
Elias Zeitfogel 2016-02-10 17:39:19 +01:00
commit 41edf6fa72
183 changed files with 2960 additions and 615 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

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
@ -99,7 +103,7 @@ if 1:
os.makedirs(lib_dir)
except OSError:
pass
Popen(['./virtualenv.py', lib_dir], cwd=t).wait()
Popen([sys.executable, './virtualenv.py', lib_dir], cwd=t).wait()
Popen([os.path.join(lib_dir, 'bin', 'pip'),
'install', '--upgrade', 'Lektor']).wait()
os.symlink(os.path.join(lib_dir, 'bin', 'lektor'),

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;;;;;;;;;;;;;AC+LA;;;;;;;;;;AAqiBA;AAqpHA;AAkgIA;AAg3EA","sourceRoot":""}
{"version":3,"file":"app.js","sources":["webpack:///app.js","webpack:///"],"mappings":"AAAA;;;;;;;;;;;;;ACyMA;;;;;;;;;;AAqiBA;AAqpHA;AAkgIA;AAg3EA","sourceRoot":""}

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

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

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,17 +15,24 @@ overview page.
The following attributes exist on the pagination object:
| Attribute | Description
| ----------- | ------------
| `current` | The current record
| `prev` | The record for the last page (might be `None`)
| `next` | The record for the net page (might be `None`)
| `total` | The total number of items across all pages
| `pages` | The total number of pages
| `page` | The number of current page
| `has_prev` | `True` if a previous page exists
| `has_next` | `True` if a next page exists
| `items` | The query that resolves to the children of the current page.
| Attribute | Description
| ------------ | ------------
| `current` | The current record
| `prev` | The record for the last page (might be `None`)
| `next` | The record for the net page (might be `None`)
| `total` | The total number of items across all pages
| `pages` | The total number of pages
| `page` | The number of current page
| `has_prev` | `True` if a previous page exists
| `has_next` | `True` if a next page exists
| `items` | The query that resolves to the children of the current page.
| `for_page()` | Returns the record for a different page.
The `for_page()` function accepts a page number and returns the record for
the other page. For more information also see the virtual path example
below.
!! *Changed in Lektor 2.0:* The `for_page()` method was added.
## Item Query Example
@ -60,3 +67,23 @@ next page link as well as the number of the current page:
{% endif %}
</div>
```
## Virtual Paths
!! *New in Lektor 2.0:* virtual paths did not exist in earlier Lektor versions.
The pagination is implemented in a way where each page in the pagination is
a virtual path below the record itself. The value of the path is just the
number of the page. So for instance to link to the second page you can just
do this:
```html+jinja
<a href="{{ '@2'|url }}">Go to Page 2</a>
```
Alternatively you can also use the `for_path()` function which returns the
entire pagination for a page:
```html+jinja
<a href="{{ this.pagination.for_page(2)|url }}">Go to Page 2</a>
```

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

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

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

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

@ -9,6 +9,12 @@ to a content file. This allows you to translate your website into many
different languages or to customize content specifically for some languages
or regions.
!!!! Alternatives are a fully functional feature in Lektor but very
underdocumented and lacking in parts. The result of this is that it might not
be working exactly as you expect in parts. In particular one of the limiting
factors is that you need to have at least one alternative at the URL root or
the system will refuse to build the website.
## Enabling Alternatives
To enable alternatives you need to extend your [Project File
@ -66,3 +72,11 @@ targeted, different files will be used. This table visualizes this:
| | ✓ | | fr | contents+fr.lr
| | | ✓ | en | contents+en.lr
| | | ✓ | fr | *missing*
## Alternatives and Paths
Alternatives have a special behavior with regards to paths. They alternative
code does not exist in the path! This can be confusing at first, but has the
advantage that they automatically work in most places as the paths are the
same for differnent alternatives. For more information see
[Alternatives and Paths :ref](../paths/#alternatives-and-paths).

View File

@ -49,7 +49,7 @@ Each page is associated with a model and a template. Each page needs to have
a model that defines which fields exist. The template by default matches the
model name but it can be overridden on a per-page basis.
So how is the model selected? Either expicitly in the `contents.lr` file
So how is the model selected? Either explicitly in the `contents.lr` file
with the `_model` key or by configuration and convention. Lektor will
select the default model based on trying things in this order:
@ -58,7 +58,7 @@ select the default model based on trying things in this order:
3. model matching the page ID
4. the model named `page`
The template is always the name of the model with `.html` as extension. If
The template is always the name of the model with `.html` as extension. It
can be overridden in the content file with the `_template` field.
## Content File Format
@ -80,11 +80,15 @@ The page body goes here
Fields are separated by three dashes `---` and follow the format `key: value`.
For values with multiple lines it's recommended to insert two newlines after
the key. To format of each field is specific to how the model is configured.
the key. The format of each field is specific to how the model is configured.
Some fields are plain text, others can be markdown syntax and more. These
fields become available for rendering in the template automatically.
**Tip:** If you want to use `---` itself in the document text, just add another
!!! If you want to use `---` itself in the document text, just add another
dash. This means `----` will render as `---` and `-----` will render as
`----` etc.
Fields prefixed with an underscore are so-called system fields. They are
provided by Lektor and customize behavior within Lektor. For a list of
available fields see [System Fields :ref](../api/db/system-fields/).

View File

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

@ -26,7 +26,7 @@ successfully*.
## The Build Pipeline
So let's cover the building first. When you use the Lektor server locally,
Lektor constantly builds our your website into static HTML files behind the
Lektor constantly builds out your website into static HTML files behind the
scenes into the default build folder. This folder is in an operating system
specific location. If you want to know where that folder is, you can use this
command:
@ -55,7 +55,7 @@ potential deployment target add a `[servers.NAME]` section. The supported
keys are `name` for an optional human readable name of the server, `enabled` to
enable or disable it (defaults to `true`) and `target` which is the URL to
publish to. Additionally one of the servers can have `default` set to `yes`
to set it as default. Here an example:
to set it as default. Here is an example:
```ini
[servers.production]
@ -93,14 +93,18 @@ the `LEKTOR_OUTPUT_PATH` environment variable.
the credentials directly in the URL. If you do this, please ensure that
you keep your project file secure as loss of the project file can mean that
people get access to your server. Alternatively you can also provide username
and password through the command line of environment variables.
and password through the command line or environment variables.
The following targets are supported for the `target` folder:
The following targets are supported for the `target` field:
* [rsync](rsync/)
* [FTP](ftp/)
* [GitHub Pages](ghpages/)
In addition, third-party plugins are available for these targets:
* [S3 :ext](https://github.com/spenczar/lektor-s3)
## Manual Deployments
If you want to manually deploy something through the favorite tool of yours

View File

@ -52,7 +52,7 @@ duplicate entries in that file are resolved.
## Using Other Tools
The FTP support in Lektor is quite rudimentary and in some situations you
might want to use a different tool like filezilla to do your synchronziations
might want to use a different tool like filezilla to do your synchronizations
instead. In that case you just need to point your FTP client to the build
folder. To find out where this is, just run this command:

View File

@ -8,7 +8,7 @@ body:
* `ghpages+https://username/repository`
A popular way to host websites for Open Source projects is the GitHub pages.
It's a free services provided by [GitHub :ext](http://github.com/) which allows
It's a free service provided by [GitHub :ext](http://github.com/) which allows
to host completely static websites through GitHub.
The way this is implemented in Lektor currently is that Lektor will force-push

View File

@ -57,14 +57,14 @@ configure the access credentials. We will get to that.
## Enabling Travis
So now that we have all that configured we need to tellt travis to build the
So now that we have all that configured we need to tell travis to build the
repository. For that just head to your [Travis-CI Profile
:ext](https://travis-ci.org/profile) and enable the repository. If it does not
show up yet, you can force a sync with the click of a button.
## Access Credentials
So how do you savely provide your credentials? Lektor accepts username and
So how do you safely provide your credentials? Lektor accepts username and
password for the `ghpages+https` transport via the `LEKTOR_DEPLOY_USERNAME`
and `LEKTOR_DEPLOY_PASSWORD` environment variables. These can be set in the
Travis-CI settings of your repository on travis-ci.org in secret so they are
@ -87,6 +87,12 @@ To solve this problem we recommend two things:
Once you have done that travis will start deploying the website on every
commit.
!!!! When copy/pasting username and password into travis please ensure that
you do not copy any leading or trailing whitespace with it. This will not
just break the build but also reveal the password in the process. For
more information see [travis-ci#4139
:ext](https://github.com/travis-ci/travis-ci/issues/4139).
## Committer Information
By default the commits to the `gh-pages` branch will be authored by a user
@ -118,3 +124,22 @@ cache:
install: "pip install Lektor"
script: "lektor build && lektor deploy ghpages"
```
## Restricting Branches
If you plan on having different branches and contributors you should disable
the deployment to the master branch only. You can do this with the following
config:
```yaml
language: python
python: 2.7
cache:
directories:
- $HOME/.cache/pip
- $HOME/.cache/lektor/builds
install: "pip install Lektor"
script:
- "lektor build"
- 'test "$TRAVIS_PULL_REQUEST" == false && test "$TRAVIS_BRANCH" == master && lektor deploy ghpages'
```

View File

@ -93,6 +93,7 @@ page.
{% extends "layout.html" %}
{% from "macros/pagination.html" import render_pagination %}
{% block title %}My Blog{% endblock %}
{% block body %}
<h1>My Blog</h1>
<ul class="blog-index">
@ -122,7 +123,7 @@ do:
{% extends "layout.html" %}
{% block title %}{{ this.title }} | My Blog{% endblock %}
{% block body %}
<1h>{{ this.title }}
<h1>{{ this.title }}
<p class="meta">
by {{ this.author }}
on {{ this.pub_date|dateformat('full') }}
@ -141,7 +142,7 @@ with the name of your blog. For instance just `content/blog` and put a
_model: blog
```
Now you can head to the admin to create new blog posts.
Now you can head to the admin UI to create new blog posts.
## Changing the URL Structure

View File

@ -63,7 +63,7 @@ source = site.query('/project-categories')
```
The above models should be mostly clear. What is probably not entirely clear
is the `source` parameter for the categories. Esentially we instruct the
is the `source` parameter for the categories. Essentially we instruct the
admin panel to fill the selection for the checkboxes from the child pages
below the `project-categories` folder. This is where we will maintain the
categories.
@ -180,7 +180,8 @@ markup that appears on multiple pages.
```html+jinja
{% extends "layout.html" %}
{% from "macros/projects.html" import render_category_nav %}
{% from "macros/projects.html" import render_category_nav,
render_project_list %}
{% block title %}Project Category {{ this.name }}{% endblock %}
{% block body %}
<h1>Project Category {{ this.name }}</h1>

View File

@ -0,0 +1,86 @@
title: Error Pages
---
summary: Brief introduction into error pages in Lektor.
---
body:
Because Lektor renders out static pages the question comes up what happens if a
page cannot be found. This is typically achieved by a special page that a
server will then use as a stand-in for a page that otherwise cannot be found.
In Lektor as a convention this page should be called `404.html`. While in
reality the name of this page largely depends on how you deploy your pages we
are sticking with the generally accepted location of calling it `404.html`.
This will work on GitHub Pages and some other environments where this cannot
be otherwise configured and most web servers can be configured to use this file
for URLs that are not found.
! In versions of Lektor before 2.0 custom 404 pages will not be honored by the
development server. To test those you will need to explicitly navigate to
`/404.html`.
## URLs on 404 Pages
If you are using 404 pages then these pages can appear at any URL. This means
that relative URLs *will not work*. If you want to use custom error pages you
will have to set the `url_style` to `absolute` as otherwise URLs on an error
page will not work. Just add this to your project file:
```ini
[project]
url_style = absolute
```
For more information about this you can read the [Project File Documentation
:ref](../../project/file/).
## Creating an Error Page
You can create easy create a 404 page by creating a `404.html/contents.lr`
file. If you do not care much about the contents and structure of the file
you can just point it to an empty model (`none`) and manually select a
`404.html` template like this:
```
_model: none
----
_template: 404.html
```
Then just create a `404.html` template with the intended contents.
## Server Configuration
If you are deploying such pages to your own servers you will need to ensure
that the error pages are activated. Depending on the server used this will
work slightly differently.
### Apache
Making custom error pages work is easiest with Apache. If `.htaccess` files
are enabled you can just put a file with that name into your `assets` folder
and add the following line to it:
```apache
ErrorDocument 404 /404.html
```
Alternatively you can add the above line into a `VirtualHost` or `Directory`
section in your main config file.
### nginx
For nginx you need to enable the error document in your main config file. Just
add it to your `server` section:
```nginx
error_page 404 /404.html;
```
### lighttpd
If you are using lighttpd you can configure an error page for 404 this way:
```ini
server.error-handler-404 = "/404.html"
```

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