RomeoGolf

Вт 19 Январь 2016

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">
      &copy; {{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, при этом, надо знать язык статьи (русский, например, но хотелось бы иногда и английский) хотя бы на четверочку, владеть словом, а для привлекательного оформления уметь рисовать и фотографировать.

Как же много требований… Но не надо сдаваться! Тем более, никто не будет подходить к оценке блога с той же позиции, что и к оценке сайта профессионала-дизайнера или серезной промышленной компании. В конце концов, можно остановиться на простом минималистичном дизайне и наращивать только удобство пользования.


Теги: