lektor-website/docs/guides/categories/index.html

426 lines
26 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="../../../static/styles.css?h=dff0aaad">
<link rel="stylesheet" href="../../../static/pygments.css">
<link rel="shortcut icon" href="../../../static/favicon.png?h=fa09bedd">
<title>Categories | Documentation | Lektor Static Content Management System</title>
</head>
<body class="default">
<nav class="navbar navbar-inverse navbar-static-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed"
data-toggle="collapse" data-target="#navbar"
aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="../../../">Lektor</a>
</div>
<div id="navbar" class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li><a href="../../../downloads/">Download</a></li>
<li class="active"><a href="../../">Documentation</a></li>
<li><a href="../../../showcase/">Showcase</a></li>
<li><a href="../../../plugins/">Plugins</a></li>
<li><a href="../../../community/">Community</a></li>
<li><a href="../../../blog/">Blog</a></li>
</ul>
</div>
</div>
</nav>
<div class="body-wrapper">
<div class="container">
<div class="row">
<div class="col-sm-3">
<ul class="tree-nav nocontent">
<li><a href="../../">Welcome</a></li>
<li><a href="../../what/">What is Lektor</a>
<li><a href="../../installation/">Installation</a>
<li><a href="../../quickstart/">Quickstart</a>
<li><a href="../../project/">Project</a>
<li><a href="../../content/">Content</a>
<li><a href="../../templates/">Templates</a>
<li><a href="../../themes/">Themes</a>
<li><a href="../">Guides</a>
<ul>
<li><a href="../blog/">Blog</a>
<li class="active"><a href="./">Categories</a>
<ul></ul>
<li><a href="../disqus/">Disqus Comments</a>
<li><a href="../error-pages/">Error Pages</a>
<li><a href="../page-order/">Page Order</a>
<li><a href="../pagination/">Pagination</a>
<li><a href="../portfolio/">Portfolio Sites</a>
<li><a href="../redirects/">Redirects</a>
<li><a href="../single-page/">Single-Page</a>
<li><a href="../sitemap/">Sitemap</a>
<li><a href="../webpack/">Webpack</a>
</ul>
<li><a href="../../deployment/">Deployment</a>
<li><a href="../../plugins/">Plugins</a>
<li><a href="../../models/">Data Modelling</a>
<li><a href="../../cli/">Command Line</a>
<li><a href="../../api/">API</a>
<li><a href="../../search/">Search</a>
</ul>
<div class="visible-md-block visible-lg-block">
<h4>This Page</h4>
<ul class="toc">
<li><a href="#basic-setup">Basic Setup</a></li>
<li><a href="#the-models">The Models</a><ul>
<li><a href="#projects.ini`"><code>projects.ini</code></a></li>
<li><a href="#project.ini`"><code>project.ini</code></a></li>
<li><a href="#project-categories.ini`"><code>project-categories.ini</code></a></li>
<li><a href="#project-category.ini`"><code>project-category.ini</code></a></li>
</ul></li>
<li><a href="#initializing-the-folders">Initializing The Folders</a><ul>
<li><a href="#projects/contents.lr`"><code>projects/contents.lr</code></a></li>
<li><a href="#project-categories/contents.lr`"><code>project-categories/contents.lr</code></a></li>
</ul></li>
<li><a href="#creating-categories">Creating Categories</a></li>
<li><a href="#the-templates">The Templates</a><ul>
<li><a href="#projects.html`"><code>projects.html</code></a></li>
<li><a href="#project.html`"><code>project.html</code></a></li>
<li><a href="#project-categories.html`"><code>project-categories.html</code></a></li>
<li><a href="#project-category.html`"><code>project-category.html</code></a></li>
<li><a href="#macros/projects.html`"><code>macros/projects.html</code></a></li>
</ul></li>
</ul>
</div>
</div>
<div class="col-sm-9 doc-styling">
<h1>Categories</h1>
<ul class=page-meta>
</ul>
<p>Because Lektor is based on the idea of mirroring the tree structure from the
file system to the final website it sometimes might not be quite clear how
one could achieve any form of filtering (for instance categories). However
this is actually very easily achieved through the concept of “child
replacement”. This will give you a quick introduction to how this feature
can be used to implement categories.</p>
<h2 id="basic-setup">Basic Setup</h2><p>In this case we assume you have a bunch of projects that can exist in
different categories and it should be possible to see which projects belong
to which category.</p>
<h2 id="the-models">The Models</h2><p>So we will end up with four models: <code>projects.ini</code>, <code>project.ini</code>,
<code>project-categories.ini</code> and <code>project-category.ini</code>. Here is how they look:</p>
<h3 id="projects.ini`"><code>projects.ini</code></h3><div class="hll"><pre><span></span><span class="k">[model]</span><span class="w"></span>
<span class="na">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">Projects</span><span class="w"></span>
<span class="na">label</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">Projects</span><span class="w"></span>
<span class="na">hidden</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">yes</span><span class="w"></span>
<span class="na">protected</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">yes</span><span class="w"></span>
<span class="k">[children]</span><span class="w"></span>
<span class="na">model</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">project</span><span class="w"></span>
<span class="na">order_by</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">-date, name</span><span class="w"></span>
</pre></div>
<h3 id="project.ini`"><code>project.ini</code></h3><div class="hll"><pre><span></span><span class="k">[model]</span><span class="w"></span>
<span class="na">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">Project</span><span class="w"></span>
<span class="na">label</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">{{ this.name }}</span><span class="w"></span>
<span class="na">hidden</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">yes</span><span class="w"></span>
<span class="k">[fields.name]</span><span class="w"></span>
<span class="na">label</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">Name</span><span class="w"></span>
<span class="na">type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">string</span><span class="w"></span>
<span class="k">[fields.date]</span><span class="w"></span>
<span class="na">label</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">Date</span><span class="w"></span>
<span class="na">type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">date</span><span class="w"></span>
<span class="k">[fields.description]</span><span class="w"></span>
<span class="na">label</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">Description</span><span class="w"></span>
<span class="na">type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">markdown</span><span class="w"></span>
<span class="k">[fields.categories]</span><span class="w"></span>
<span class="na">label</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">Categories</span><span class="w"></span>
<span class="na">type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">checkboxes</span><span class="w"></span>
<span class="na">source</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">site.query(&#39;/project-categories&#39;)</span><span class="w"></span>
</pre></div>
<p>The above models should be mostly clear. What is probably not entirely clear
is the <code>source</code> parameter for the categories. Essentially we instruct the
admin panel to fill the selection for the checkboxes from the child pages
below the <code>project-categories</code> folder. This is where we will maintain the
categories.</p>
<h3 id="project-categories.ini`"><code>project-categories.ini</code></h3><div class="hll"><pre><span></span><span class="k">[model]</span><span class="w"></span>
<span class="na">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">Project Categories</span><span class="w"></span>
<span class="na">label</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">Project Categories</span><span class="w"></span>
<span class="na">hidden</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">yes</span><span class="w"></span>
<span class="na">protected</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">yes</span><span class="w"></span>
<span class="k">[children]</span><span class="w"></span>
<span class="na">model</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">project-category</span><span class="w"></span>
<span class="na">order_by</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">name</span><span class="w"></span>
</pre></div>
<h3 id="project-category.ini`"><code>project-category.ini</code></h3><div class="hll"><pre><span></span><span class="k">[model]</span><span class="w"></span>
<span class="na">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">Project Category</span><span class="w"></span>
<span class="na">label</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">{{ this.name }}</span><span class="w"></span>
<span class="na">hidden</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">yes</span><span class="w"></span>
<span class="k">[children]</span><span class="w"></span>
<span class="na">replaced_with</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">site.query(&#39;/projects&#39;).filter(F.categories.contains(this))</span><span class="w"></span>
<span class="k">[fields.name]</span><span class="w"></span>
<span class="na">label</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">Name</span><span class="w"></span>
<span class="na">type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">string</span><span class="w"></span>
</pre></div>
<p>So this is where the magic lives. the <code>replaced_with</code> key in the <code>[children]</code>
section tells Lektor to ignore the child pages that would normally exist below
the path but to substitute them with another query. In this case we replace
them with all the projects where the category (<code>this</code>) is in the checked
set of categories (<code>F.categories</code>).</p>
<h2 id="initializing-the-folders">Initializing The Folders</h2><p>Now that we have the models, we need to initialize the folders. This is
necessary because most models are hidden which means they cannot be selected
from the admin interface. We need the following things:</p>
<h3 id="projects/contents.lr`"><code>projects/contents.lr</code></h3><pre><code>_model: projects
</code></pre>
<h3 id="project-categories/contents.lr`"><code>project-categories/contents.lr</code></h3><pre><code>_model: project-categories
---
_slug: projects/categories
</code></pre>
<p>Here we also tell the system that we want to move the page from
<code>project-categories/</code> to <code>projects/categories/</code> which looks nicer. This
means a category named <code>Foo</code> will then end up as <code>projects/categories/foo/</code>.</p>
<h2 id="creating-categories">Creating Categories</h2><p>Now we can head to the admin panel to create some categories. Each category
that is created will become available for new projects in the category
selection.</p>
<h2 id="the-templates">The Templates</h2><p>To render out all the pages we probably want to use some macros to reuse
markup that appears on multiple pages.</p>
<h3 id="projects.html`"><code>projects.html</code></h3><div class="hll"><pre><span></span><span class="cp">{%</span> <span class="k">extends</span> <span class="s2">&quot;layout.html&quot;</span> <span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">from</span> <span class="s2">&quot;macros/projects.html&quot;</span> <span class="k">import</span>
<span class="nv">render_project_list</span><span class="o">,</span> <span class="nv">render_category_nav</span> <span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">block</span> <span class="nv">title</span> <span class="cp">%}</span>Projects<span class="cp">{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">block</span> <span class="nv">body</span> <span class="cp">%}</span>
<span class="p">&lt;</span><span class="nt">h1</span><span class="p">&gt;</span>Projects<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
<span class="cp">{{</span> <span class="nv">render_category_nav</span><span class="o">(</span><span class="nv">active</span><span class="o">=</span><span class="kp">none</span><span class="o">)</span> <span class="cp">}}</span>
<span class="cp">{{</span> <span class="nv">render_project_list</span><span class="o">(</span><span class="nv">this.children</span><span class="o">)</span> <span class="cp">}}</span>
<span class="cp">{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
</pre></div>
<h3 id="project.html`"><code>project.html</code></h3><div class="hll"><pre><span></span><span class="cp">{%</span> <span class="k">extends</span> <span class="s2">&quot;layout.html&quot;</span> <span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">block</span> <span class="nv">title</span> <span class="cp">%}{{</span> <span class="nv">this.name</span> <span class="cp">}}{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">block</span> <span class="nv">body</span> <span class="cp">%}</span>
<span class="p">&lt;</span><span class="nt">h1</span><span class="p">&gt;</span><span class="cp">{{</span> <span class="nv">this.name</span> <span class="cp">}}</span><span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&quot;description&quot;</span><span class="p">&gt;</span><span class="cp">{{</span> <span class="nv">this.description</span> <span class="cp">}}</span><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
<span class="cp">{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
</pre></div>
<h3 id="project-categories.html`"><code>project-categories.html</code></h3><div class="hll"><pre><span></span><span class="cp">{%</span> <span class="k">extends</span> <span class="s2">&quot;layout.html&quot;</span> <span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">from</span> <span class="s2">&quot;macros/projects.html&quot;</span> <span class="k">import</span> <span class="nv">render_category_nav</span> <span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">block</span> <span class="nv">title</span> <span class="cp">%}</span>Project Categories<span class="cp">{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">block</span> <span class="nv">body</span> <span class="cp">%}</span>
<span class="p">&lt;</span><span class="nt">h1</span><span class="p">&gt;</span>Project Categories<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
<span class="cp">{{</span> <span class="nv">render_category_nav</span><span class="o">(</span><span class="nv">active</span><span class="o">=</span><span class="kp">none</span><span class="o">)</span> <span class="cp">}}</span>
<span class="cp">{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
</pre></div>
<h3 id="project-category.html`"><code>project-category.html</code></h3><div class="hll"><pre><span></span><span class="cp">{%</span> <span class="k">extends</span> <span class="s2">&quot;layout.html&quot;</span> <span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">from</span> <span class="s2">&quot;macros/projects.html&quot;</span> <span class="k">import</span> <span class="nv">render_category_nav</span><span class="o">,</span>
<span class="nv">render_project_list</span> <span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">block</span> <span class="nv">title</span> <span class="cp">%}</span>Project Category <span class="cp">{{</span> <span class="nv">this.name</span> <span class="cp">}}{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">block</span> <span class="nv">body</span> <span class="cp">%}</span>
<span class="p">&lt;</span><span class="nt">h1</span><span class="p">&gt;</span>Project Category <span class="cp">{{</span> <span class="nv">this.name</span> <span class="cp">}}</span><span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
<span class="cp">{{</span> <span class="nv">render_category_nav</span><span class="o">(</span><span class="nv">active</span><span class="o">=</span><span class="nv">this._id</span><span class="o">)</span> <span class="cp">}}</span>
<span class="cp">{{</span> <span class="nv">render_project_list</span><span class="o">(</span><span class="nv">this.children</span><span class="o">)</span> <span class="cp">}}</span>
<span class="cp">{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
</pre></div>
<h3 id="macros/projects.html`"><code>macros/projects.html</code></h3><div class="hll"><pre><span></span><span class="cp">{%</span> <span class="k">macro</span> <span class="nv">render_category_nav</span><span class="o">(</span><span class="nv">active</span><span class="o">=</span><span class="kp">none</span><span class="o">)</span> <span class="cp">%}</span>
<span class="p">&lt;</span><span class="nt">ul</span><span class="p">&gt;</span>
<span class="cp">{%</span> <span class="k">for</span> <span class="nv">category</span> <span class="k">in</span> <span class="nv">site.query</span><span class="o">(</span><span class="s1">&#39;/project-categories&#39;</span><span class="o">)</span> <span class="cp">%}</span>
<span class="p">&lt;</span><span class="nt">li</span><span class="cp">{%</span> <span class="k">if</span> <span class="nv">category._id</span> <span class="o">==</span> <span class="nv">active</span> <span class="cp">%}</span> <span class="na">class</span><span class="o">=</span><span class="s">&quot;active&quot;</span><span class="cp">{%</span> <span class="k">endif</span>
<span class="cp">%}</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&quot;</span><span class="cp">{{</span> <span class="nv">category</span><span class="o">|</span><span class="nf">url</span> <span class="cp">}}</span><span class="s">&quot;</span><span class="p">&gt;</span><span class="cp">{{</span> <span class="nv">category.name</span> <span class="cp">}}</span><span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
<span class="cp">{%</span> <span class="k">endfor</span> <span class="cp">%}</span>
<span class="p">&lt;/</span><span class="nt">ul</span><span class="p">&gt;</span>
<span class="cp">{%</span> <span class="k">endmacro</span> <span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">macro</span> <span class="nv">render_project_list</span><span class="o">(</span><span class="nv">projects</span><span class="o">)</span> <span class="cp">%}</span>
<span class="p">&lt;</span><span class="nt">ul</span><span class="p">&gt;</span>
<span class="cp">{%</span> <span class="k">for</span> <span class="nv">project</span> <span class="k">in</span> <span class="nv">projects</span> <span class="cp">%}</span>
<span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&quot;</span><span class="cp">{{</span> <span class="nv">project</span><span class="o">|</span><span class="nf">url</span> <span class="cp">}}</span><span class="s">&quot;</span><span class="p">&gt;</span><span class="cp">{{</span> <span class="nv">project.name</span> <span class="cp">}}</span><span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
<span class="cp">{%</span> <span class="k">endfor</span> <span class="cp">%}</span>
<span class="p">&lt;/</span><span class="nt">ul</span><span class="p">&gt;</span>
<span class="cp">{%</span> <span class="k">endmacro</span> <span class="cp">%}</span>
</pre></div>
<div class="comment-box">
<h2>Comments</h2>
<div id="disqus_thread"></div>
<script>
var disqus_config = function() { this.page.identifier = "/docs/guides/categories"; this.page.url = "https://www.getlektor.com/docs/guides/categories/"; };
(function() {
var d = document, s = d.createElement('script');
s.src = '//lektordocumentation.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>
Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript"
rel="nofollow">comments powered by Disqus.</a>
</noscript>
</div>
</div>
</div>
</div>
</div>
<div class="bottomsummary">
<div class="container">
</div>
</div>
<footer>
<div class="container">
<div class="row">
<div class="col-sm-4 icon-bar">
<a href="https://github.com/lektor/lektor/" title="Lektor on GitHub"
><i class="fa fa-github"></i></a>
<a href="https://github.com/lektor/lektor/issues/" title="Report Issues for Lektor"
><i class="fa fa-bug"></i></a>
<a href="https://twitter.com/getlektor" title="Find Lektor on Twitter"
><i class="fa fa-twitter"></i></a>
<a href="https://gitter.im/lektor/lektor" title="Chat on Gitter"
><i class="fa fa-comment"></i></a>
<a href="https://github.com/lektor/lektor-website/tree/master/content/docs/guides/categories/contents.lr" title="View source for this page"><i class="fa fa-code"></i></a>
</div>
<div class="col-sm-8">
<a href="../../../license/">License & Copyright</a>
<a href="../../../contact/">Contact</a>
Made with <i class="fa fa-fw fa-heart" title="Heart"><span hidden>Heart</span></i> in Carinthia
</div>
</div>
</div>
</footer>
<script type=text/javascript src="../../../static/app.js?h=ee15141e" charset="utf-8"></script>
<script>
((window.gitter = {}).chat = {}).options = {
room: 'lektor/lektor',
activationElement: null
};
document.write('<button class="js-gitter-toggle-chat-button">Toggle Chat</button>');
var dnt = navigator.doNotTrack || window.doNotTrack || navigator.msDoNotTrack;
if (dnt != "1" && dnt != "yes") {
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
ga('create', 'UA-70822533-1', 'auto');
ga('set', 'anonymizeIp', true);
ga('send', 'pageview');
} else {
console.debug("Respecting Do-Not-Track, not running analytics.");
}
</script>
<script async src='https://www.google-analytics.com/analytics.js'></script>
<script async defer id="github-bjs" src="https://buttons.github.io/buttons.js"></script>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script>
<script src="https://sidecar.gitter.im/dist/sidecar.v1.js" async defer></script>
</body>
</html>