Pelican и новая тема
Начнем с нуля
Тема notmyidea сама по себе выглядит неплохо и вполне допускает правку под свои нужды. Но поступим экстремальнее — сделаем всё с самого начала. В корне проекта сайта, в нашем случае — D:\site\
, создаем папку themes, там делаем папку nanotheme, а в ней — папки static и templates. Почему nano? Во-первых, это модно, во-вторых, в этой теме будет предельный минимум, только для примера, чтобы хоть как-то работало.
В папке templates создадим base.html, текстовый файл в UTF-8. Непременный, обязательный базовый шаблон, который будет определять общую разметку для всех страниц, подчиняющихся нашей теме. Ну, и набросаем общий внешний вид:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>будет заголовок</title>
</head>
<body>
<header class="main-header">
будет хедер
</header>
<nav>
Будет главное меню
</nav>
<div class="content">
Будет контент
</div>
<div class="footer">
будет футер
</div>
</body>
</html>
Если этот файл посмотреть в браузере, то можно увидеть тупо четыре строчки:
будет хедер
Будет главное меню
Будет контент
будет футер
А если запустить генерацию страниц сайта, то в результате мы вообще не увидим изменений. Оно и понятно — тема-то в настройках все еще подключена старая. Давайте переподключим. Найдем в pelicanconf.py строчку
THEME = 'notmyidea'
и заменим ее на
THEME = 'themes/nanotheme'
Как вариант, можно не заменять, а поставить в начале исходной строки знак комментария #, а новую просто поставить чуть ниже (если хотите — чуть выше).
И вот теперь после генерации сайта мы увидим изменения. А именно: мы увидим эти же четыре строчки, что и в base.html, на любой сгенерированной странице сайта. Это не то, что нам бы хотелось, но именно то, что мы заказывали. Значит, надо научиться заказывать то, что мы хотим.
От простой разметки — к шаблону
Для того, чтобы сделать из примитивной веб-странички примитивный шаблон для Pelican, надо использовать jinja2 — движок сайтовых шаблонов для Python. Вставляя конструкции дзиндзя в текст разметки, мы даем генератору сайта понять, в каком месте шаблона мы хотим видеть некоторую информацию, и где ее взять. Строго говоря, эти конструкции будут работать в любом языке разметки, но, раз уж делаем сайт, нас интересует конкретно HTML.
Самыми употребимыми (глядя на код шаблонов нескольких скачанных тем) будут блоки и переменные.
<title>{% block title %}{% endblock %}</title>
<h1>{{ a_title }}</h1>
Первая конструкция позволяет в дочернем шаблоне создать именованный блок (с именем title), и содержимое этого блока будет подставлено на место, определенное в родительском шаблоне. Вторая конструкция вместо переменной языка Python вставит ее значение. А значение, в свою очередь, может быть, например, задано в файле конфигурации pelicanconf.py или определяться при помощи метаданных статьи или страницы.
Вообще-то, блоки являются подмножеством операторов, а переменные — подмножеством выражений:
- {% … %} — для операторов
- {{ … }} — для выражений (печать вывода шаблона)
- {# … #} — для комментариев, которые не включаются в вывод шаблона
- # … ## — для линейных выражений (в отличие от блочных, на одну строку)
но подробный разбор синтаксиса jinja2 займет слишком много места и времени, чтобы заниматься им прямо здесь и прямо сейчас.
Давайте попробуем воткнуть парочку блоков и переменных. Что касается переменных — как минимум, мы имеем в настройках AUTHOR и SITENAME. Типовые названия некоторых блоков можно подсмотреть в других темах. Итак, правим базовый шаблон:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>{% block title %}{{SITENAME}}{% endblock %}</title>
{% block head %}{% endblock %}
</head>
<body>
<header class="main-header">
<div>
<a id="name" href={{ SITEURL }} id="home-link">{{ SITENAME }}</a>
</div>
</header>
<nav>
<ul class="main-nav">
{% for cat, null in categories %}
<li {% if cat == category %}class="active"{% endif %}>
<a href="{{ SITEURL }}/{{ cat.url }}">{{ cat }}</a>
</li>
{% endfor %}
{% if DISPLAY_PAGES_ON_MENU %}
{% for current_page in PAGES %}
<li {% if current_page == page %}class="active"{% endif %}>
<a href="{{ SITEURL }}/{{ current_page.url }}">{{ current_page.title }}</a>
</li>
{% endfor %}
{% endif %}
</ul>
</nav>
<div class="content">
{% block main_section %}{% endblock %}
</div>
<div class="footer">
© {{AUTHOR}}
</div>
</body>
</html>
В результате base.html будет выглядеть уже так:
{% block head %}{% endblock %} {{ SITENAME }} {% for cat, null in categories %} {{ cat }} {% endfor %} {% if DISPLAY_PAGES_ON_MENU %} {% for current_page in PAGES %} {{ current_page.title }} {% endfor %} {% endif %} {% block main_section %}{% endblock %} © {{AUTHOR}}
а откомпилированные страницы сайта, соответственно, так:
Site Разное Рыба О сайте © Me
причем, ссылки «меню» кликабельны, что можно увидеть по изменению адреса в адресной строке браузера. Но все страницы выглядят совершенно одинаково.
В этом шаблоне мы:
- в теге
<title>
указали заголовок сайта, отображаемый в панели и вкладке браузера — взяли его из переменной SITENAME, указанной в pelicanconf.py; - в теге
<header>
сделали заголовок сайта из этой же переменной; - в теге
<nav>
изобразили меню сайта:- сперва при помощи оператроа
for
перечислили все категории, которые Python наковырял из метаданных наших статей, причем, обернули названия категорий в тег<a>
, чтобы получились ссылки; - затем при помощи оператора
if
проверили переменную, заданную в файле конфигурации, которая разрешает указание в меню не только статей, но и страниц; - убедившись, что страницы тоже можно перечислить в меню, опять же при помощи
for
сделали список страниц, которых у нас пока одна — «О сайте»;
- сперва при помощи оператроа
- в html-блоке
content
добавили jinja-блокmain_section
— для наследников; - в футере нарисовали значок копирайта с указанием автора, а автора взяли опять же из файла конфигурации, переменная AUTHOR.
Добавляем шаблоны
Но у нас нет шаблона для главной страницы сайта, которая index, а также для страницы со списком категорий, тегов и, собственно, самих статей, поэтому все страницы одинаковые — к ним применяется единственный шаблон. Сделаем index.html. В нем первым делом укажем, что он расширяет базовый. Потом в main_section
перечислим имеющиеся статьи, запихав их в соответствующие html-теги, не забыв указать краткое содержание (article.summary). Должно получиться примерно так:
{% extends 'base.html' %}
{% block main_section %}
<section class="main-section blog-section" id="blog-posts">
{% for article in articles_page.object_list %}
<h3 class="date">{{article.locale_date}}</h3>
<article>
<h2>
<a href="{{SITEURL}}/{{article.url}}">{{article.title}}
</a>
</h2>
{{article.summary}}
</article>
{% endfor %}
</section>
{% endblock %}
Шаблон для статей будет называться article.html, тоже расширяет базовый. В заголовке мы его попросим показать не имя сайта, а название статьи. В главной секции укажем дату статьи (сформированную в соответствии с локалью), заголовок статьи и сам текст (article.content). После текста статьи перечислим теги, если они есть, то есть, сперва проверим наличие, а потом нарисуем список, в котором каждый тег сделан ссылкой на список статей с таким тегом:
{% extends 'base.html' %}
{% block title %}{{article.title}}{% endblock %}
{% block main_section %}
<section class="main-section blog-section" id="blog-posts">
<h3 class="date">{{article.locale_date}}</h3>
<article>
<h2>{{article.title}}</a></h2>
{{article.content}}
<br/>
<footer>
{% if article.tags %}
<div>
<div>Теги:</div>
<div class="taglist">
<ul class="tags">
{% for tag in article.tags %}
<li><a href="{{ SITEURL }}/{{ tag.url }}" class="tag">{{tag}}</a></li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
</footer>
</article>
</section>
{% endblock %}
Для страниц, точнее, для пока единственной нашей страницы, нужен шаблон page.html, похожий на шаблон статей, только проще, без тегов:
{% extends "base.html" %}
{% block title %}{{ page.title }}{% endblock %}
{% block main_section %}
<section class="main-section">
<h1 class="entry-title">{{ page.title }}</h1>
<br /><br />
{{ page.content }}
</section>
{% endblock %}
Ну, и для выбора страниц по тегу нужен шаблон tag.html:
{% extends 'base.html' %}
{% block title %}Статьи с тегом '{{tag}}' | {{ SITENAME }} {% endblock %}
{% block main_section %}
<section class="main-section blog-section" id="blog-posts">
{% for article in articles %}
<h3 class="date">{{article.locale_date}}</h3>
<article>
<h2><a href="{{SITEURL}}/{{article.url}}">{{article.title}}</a></h2>
{{article.summary}}
</article>
{% endfor %}
</section>
{% endblock %}
Итак, D:\site\themes\nanotheme\templates\
содержит пять файлов шаблонов. Вернемся в корень проекта сайта и запустим сборку. Теперь гиперссылки работают вменяемо. Ну, название сайта по-прежнему открывает содержимое корневой папки, но это нормально. Зато заголовок статьи ведет на страницу статьи, теги открывают страницу со списком статей с таким тегом, категории — на страницу со списком статей в данной категории.
Ой! А почему? Мы же не делали шаблон для categories.html, а он работает! Это как? А так. Документация рассказывает нам (правда, по-английски), что если какого-то из настоятельно необходимых шаблонов не окажется в папке templates, то вместо него будет использоваться соответствующий шаблон темы simple. И вообще, дескать, если вас устраивает HTML-структура шаблона этой умолчательной темы, то не надо писать собственных велосипедов. Тем более, что можно унаследоваться от шаблонов этой темы и чутка расширить их. Ну что ж, в отношении шаблона categories так и поступим.
Подключение стилей
У нас теперь есть разметка разного рода страниц сайта. Но нет оформления, какая же это тема? Да никакая. Чтобы она стала хоть какая-нибудь, надо ей добавить стили. Значит, идем в папку D:\site\themes\nanotheme\static\
и делаем там папку css
, а в ней файл styles.css:
body { /* установим серенький цвет фона и размер полей */
background-color: #CCC;
margin: 25px;
}
.main-header, .content, .footer {
padding: 10px; /* внутриблочные отступы до границы */
margin: 20px; /* отступы между блоками страницы */
outline: solid 1px #000; /* черненькая тоненькая рамочка */
background-color: #FFF; /* и белый цвет фона */
min-width: 600px; /* позволим тянуться, */
max-width: 800px; /* но в определенных пределах */
}
.main-header {
height: 200px; /* ограничим высоту */
background-color: #aaa; /* и подменим цвет */
}
h1 a:hover {
/* чтобы ссылка-заголовок при наведении не меняла цвет */
background-color: inherit
}
#name {
/* для текста названия сайта - размер, шрифт и рюшечки */
font-size: 350%;
font-family: Arial;
text-decoration: none; /* Убираем подчёркивание */
color: #fff; /* Цвет текста белый */
}
.main-nav {
height: 30px;
margin-top: -50px;
}
.main-nav li {
display: inline-block; /* Строчно-блочные элементы */
background-color: #551111; /* Цвет фона */
margin-right: 3px; /* Расстояние между пунктами меню */
}
.main-nav a {
color: #fff; /* Цвет ссылок */
display: block; /* Блочный элемент */
padding: 5px 15px; /* Поля вокруг текста */
text-decoration: none; /* Убираем подчёркивание */
}
.main-nav li:hover {
background-color: #881144; /* Цвет фона при наведении курсора мыши */
}
.main-nav li.active {
background-color: #881144; /* Цвет фона при наведении курсора мыши */
}
.tags li {
/* для списка тегов к статье, чтобы они были в одну строку */
display: inline-block; /* Строчно-блочные элементы */
margin-right: 3px; /* Расстояние между пунктами меню */
}
Надо еще не забыть подключить эти стили в базовом шаблоне base.html, например, сразу после тега <title>
:
<link rel="stylesheet" href="{{ SITEURL }}/theme/css/styles.css" type="text/css" />
Ни в коем случае не претендую на то, что создал прекрасный образец стиля. Цветовая гамма сомнительная, решение задать размер шрифта в процентах спорное. Однако такого CSS достаточно для того, чтобы показать: тема заработала так, как мы ожидали.
В нее еще многое можно напихать. Добавить шаблоны страниц для категорий и авторов, обозначить поведение, связанное с архивацией старых статей, включить ограничение количества статей на странице, дорисовать сайдбар (а может, и два, с обеих сторон), добавить плагины для облака тегов или карты сайта, нарисовать фон, логотип и иконку favicon, подобрать вменяемый набор цветов для оформления… В общем, хватает работы для дизайнера, верстальщика, контент-менеджера и всех остальных работников веб-студии, которых приходится представлять в единственном числе в собственном лице.
Всё еще впереди
Чтобы сделать «идеальный» по своим требованиям шаблон (с точки зрения функциональности), надо хорошо представлять себе настройки Pelican и хотя бы в общих чертах иметь представление о jinja2.
Чтобы сделать качественную разметку шаблона (с точки зрения внешнего вида), надо иметь знания в области HTML, CSS, местами — JavaScript, и в общих чертах представлять, как это все работает в разных браузерах.
Чтобы сайт не вызывал отвращения у посетителей, надо быть хоть немного дизайнером, ну, или хотя бы обладать зачатками вкуса. К сожалению, этот параметр трудно оценить у себя самостоятельно…
Чтобы наполнять сайт контентом, надо уметь пользоваться языком разметки Markdown, но местами придется вставлять и HTML, при этом, надо знать язык статьи (русский, например, но хотелось бы иногда и английский) хотя бы на четверочку, владеть словом, а для привлекательного оформления уметь рисовать и фотографировать.
Как же много требований… Но не надо сдаваться! Тем более, никто не будет подходить к оценке блога с той же позиции, что и к оценке сайта профессионала-дизайнера или серезной промышленной компании. В конце концов, можно остановиться на простом минималистичном дизайне и наращивать только удобство пользования.