Статьи

Sculpin Extended: настройка вашего статического блога на сайте

Если вы являетесь разработчиком PHP и в настоящее время ведете блог со статическим генератором сайтов, таким как Octopress или Jekyll, не было бы замечательно, если бы вы могли использовать для этого свой основной язык? Да, для нас, разработчиков, полезно использовать более одного языка, но давайте будем честными — мы часто хотим добавить некоторые функциональные возможности в наши блоги, но это трудно сделать в незнакомом синтаксисе. В этой статье мы настроим Sculpin, генератор статического сайта для PHP. Как и любой другой статический генератор сайтов, он использует файлы разметки и HTML-шаблоны для создания вашего блога, поэтому переход должен быть легким.

Логотип Sculpin

Установка Sculpin

Установка требует использования командной строки и установленной современной версии PHP (желательно 5.6 и выше).

wget https://download.sculpin.io/sculpin.phar 

После завершения загрузки мы делаем его исполняемым:

 chmod +x sculpin.phar 

Затем мы перемещаем его в каталог исполняемых файлов нашей операционной системы:

 sudo mv sculpin.phar /usr/local/bin/sculpin 

Кроме того, вы можете просто скопировать его в любой каталог, где вы хотите его использовать.

При выполнении команды sculpin будет список команд, которые вы можете использовать:

коммандные скульптуры

Создание блога

Теперь, когда у вас установлен Sculpin, мы можем создать новый блог. Мы будем использовать Sculpin Blog Skeleton, предоставленный командой Sculpin . myblog репозиторий и загрузите его в myblog .

 git clone https://github.com/sculpin/sculpin-blog-skeleton.git myblog 

Когда закончите клонирование, перейдите в myblog и запустите программу sculpin install . Это установит все зависимости, необходимые для запуска Sculpin.

 cd myblog sculpin install 

Эта команда использует Composer за кулисами (вы можете открыть sculpin.json чтобы увидеть сходство). Sculpin фактически включает в себя альтернативную версию Composer, поэтому Composer не нужен вне ее. Если вам нужно установить сторонние пакеты, просто добавьте их в require объект, а затем выполните команду sculpin install для их установки. По умолчанию пакеты устанавливаются в каталоге source/components .

Как только Sculpin завершит установку пакетов, выполните следующую команду для создания сайта. Опция watch означает, что сайт будет автоматически перегенерирован после того, как вы измените любой из файлов конфигурации или файлов внутри source каталога. Опция server запускает сервер, который будет обслуживать сгенерированный сайт. По умолчанию доступ к нему можно получить по адресу http://localhost:8000 , но если вы используете хорошую среду виртуальной машины, настройка будет зависеть от ваших открытых портов. Посмотрите этот пост о Spress, альтернативе Sculpin, чтобы узнать, как настроить его на виртуальной машине .

 sculpin generate --watch --server 

Созданный сайт будет сохранен в папке output_dev . Это используется только в целях разработки. Если вы уже собираетесь развернуть свой блог на что-то вроде страниц Github , вы должны указать опцию env :

 sculpin generate --env prod 

Это создаст каталог output_prod который вы можете затем output_prod на страницы Github. Мы пройдем через это в следующем разделе.

Конфигурирование Sculpin

Мы можем настроить Sculpin, просматривая два файла в каталоге app/config :

  • sculpin_site.yml позволяет нам редактировать глобальные параметры, такие как название блога и наше имя пользователя disqus.
  • sculpin_kernel.yml позволяет нам указать тему, которую будет использовать Sculpin, и формат постоянных ссылок.

Другие файлы конфигурации, такие как файл для развертывания корзины Amazon S3, находятся в корне каталога. Мы не будем использовать Amazon S3 в этом руководстве, поэтому мы можем оставить все как есть.

Вы можете найти больше информации о том, как настроить Sculpin, посмотрев документацию по конфигурации .

Основы блогов

Если вы выходите из Octopress или Jekyll, вы будете чувствовать себя как дома со Sculpin, потому что он использует файлы уценки для сообщений в блоге, только вместо .markdown вам придется использовать более стандартный .md в качестве расширения файла. Следует также отметить, что в Sculpin CLI нет команды для создания нового файла уценки для размещения сообщения в блоге. Это означает, что вам придется использовать команду touch для создания нового файла:

 touch 2016-05-17-getting-started-with-sculpin.md 

Напечатать это действительно очень touch.php , поэтому вот файл touch.php который вы можете поместить в корень каталога вашего блога:

 <?php $date = date('Ym-d'); if(!empty($argv[2])){ $date = $argv[2]; } $file = 'source/_posts/' . $date . '-' . str_replace('_', '-', $argv[1]) . '.md'; $title = ucwords(str_replace('_', ' ', $argv[1])); $handle = fopen($file, 'w'); $data = "--- title: {$title} tags: [] categories: [] --- "; fwrite($handle, $data); 

С этим. Вы можете выполнить следующую команду, чтобы создать новую запись в блоге:

 php touch.php getting_started_with_sculpin 

И он создаст source/_posts/2016-05-17-getting-started-with-sculpin.md файл source/_posts/2016-05-17-getting-started-with-sculpin.md .

Если вы хотите опубликовать пост-пост с датой, вы можете указать дату в качестве второго аргумента:

 php touch.php getting_started_with_sculpin 2017-01-01 

Это также добавит в содержание шаблона для вас:

 --- title: Getting Started With Sculpin tags: [] categories: [] --- 

Настройка сайта

Теперь пришло время настроить сайт так, чтобы он выглядел именно так, как мы этого хотим. Вот как будет выглядеть окончательный результат:

Пример блога Sculpin

В первую очередь мы будем работать в каталоге с source текстами, поэтому идем дальше и вводим его. Оказавшись там, откройте файл index.html . Это файл, который Sculpin обслуживает для домашней страницы сайта. В первых нескольких строках вы увидите метаблок, который используется для указания макета, заголовка страницы, генератора, используемого на странице, и количества сообщений, отображаемых на странице.

 --- layout: default title: Home generator: pagination pagination: max_per_page: 3 --- 

Если вы когда-либо использовали Twig, Blade или любой другой движок шаблонов, макеты должны быть довольно знакомы. index.html наследуется от макета по default . Макеты хранятся в папке _layouts . Шаблоны используют HTML-файлы, поэтому фактический файл будет _layouts/default.html . Откройте этот файл, очистите его содержимое и вставьте следующее:

 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>{% block title %}{{ page.title }}{% endblock %} &mdash; {{ site.title }} &mdash; {{ site.subtitle }}</title> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/picnic/4.1.2/picnic.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/picnic/4.1.2/plugins.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.4.0/styles/solarized-dark.min.css"> <link href="{{ site.url }}/css/style.css" rel="stylesheet" type="text/css" /> <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.4.0/highlight.min.js"></script> <script>hljs.initHighlightingOnLoad();</script> </head> <body> <header> <nav> <a href="/" class="brand"> <img class="logo" src="{{ site.url }}/images/jackson/76x76.png" /> <span>{{ site.title }}</span> </a> <input id="bmenub" type="checkbox" class="show"> <label for="bmenub" class="burger pseudo button">menu</label> <div class="menu"> <a href="{{ site.url }}/blog">Archives</a> <a href="{{ site.url }}/about">About</a> </div> </nav> </header> <div id="wrapper"> <main> {% block content_wrapper %} {% block content %}{% endblock %} {% endblock %} </main> <footer class="container"> &copy; {{ "now"|date("Y") }} {{ site.title }} </footer> </div> </body> </html> 

Это основной макет, который мы будем использовать. Просто сконцентрируйтесь на коде внутри <main> . Мы демистифицируем остальных позже.

Изменяется только содержимое внутри <main> . Это значит: визуализировать блок с именем content_wrapper , а внутри него визуализировать блок с именем content . Именно здесь Twig действительно сияет, потому что мы можем переопределить содержимое внутреннего блока ( content ), если определим внешний блок ( content_wrapper ). Мы увидим это в действии, когда посмотрим на файл _views/post.html позже. Пока просто помните, что в шаблонах, которые будут наследоваться от этого шаблона, должны быть определены либо content_wrapper либо блок content .

 <main> {% block content_wrapper %} {% block content %}{% endblock %} {% endblock %} </main> 

Ранее мы установили, что index.html наследуется от layouts/default.html . Но если вы откроете файл index.html , вы увидите, что мы на самом деле не определили блок content . Это потому, что Sculpin считает все, что вы добавляете после метаблока, содержимым. Там есть контент по умолчанию, добавленный скелетным сайтом, но мы будем использовать контент ниже. Давайте заменим значение по умолчанию следующим:

 {% for post in page.pagination.items %} <article> <header> <h2><a href="{{ site.url }}{{ post.url }}">{{ post.title }}</a></h2> <span class="date">{{ post.date|date('M dS, Y') }}</span> </header> <div> {% set break_array = post.blocks.content|split('<!-- more -->', 2) %} {{ break_array[0]|raw }} {% if break_array|length > 1 %} <p> <a href="{{ site.url }}{{ post.url }}"> Read more </a> </p> {% endif %} </div> </article> {% endfor %} {% if page.pagination.previous_page or page.pagination.next_page %} <div class="nav-container"> {% if page.pagination.previous_page %} <a href="{{ site.url }}{{ page.pagination.previous_page.url }}" class="right">Newer Posts</a> {% endif %} {% if page.pagination.next_page %} <a href="{{ site.url }}{{ page.pagination.next_page.url }}" class="left">Older Posts</a> {% endif %} </div> {% endif %} 

Это подводит нас к следующей теме: генераторы . Из коробки у Sculpin есть генератор, называемый нумерацией страниц, который позволяет легко разбивать страницы на коллекции. В этом случае коллекция состоит из сообщений, которые в данный момент находятся в каталоге source/_posts . Код ниже перебирает сообщения на основе номера текущей страницы. На странице указателя будут возвращены только последние три сообщения (сообщения упорядочены от самых новых до самых старых).

 {% for post in page.pagination.items %} <article> <!-- contents for each post here --> </article> {% endfor %} 

Чтобы показать пагинационные ссылки:

 {% if page.pagination.previous_page or page.pagination.next_page %} <div class="nav-container"> <!-- pagination links here --> </div> {% endif %} 

Количество сообщений, отображаемых на странице, можно указать, обновив значение для поля max_per_page в используемом шаблоне.

 pagination: max_per_page: 3 

Возвращаясь к коду, который мы показываем для каждой итерации цикла разбиения на страницы, мы имеем следующее:

 <header> <h2><a href="{{ site.url }}{{ post.url }}">{{ post.title }}</a></h2> <span class="date">{{ post.date|date('M dS, Y') }}</span> </header> <div> {% set break_array = post.blocks.content|split('<!-- more -->', 2) %} {{ break_array[0]|raw }} {% if break_array|length > 1 %} <p> <a href="{{ site.url }}{{ post.url }}"> Read more </a> </p> {% endif %} </div> 

Это отображает заголовок поста, его содержание и ссылку « читать дальше» . Ссылка read more реализуется путем создания переменной с именем break_array которой первая половина и вторая половина статьи хранятся в виде двух отдельных элементов массива. Это выглядит как комментарий <!-- more --> в качестве разделителя. HTML-комментарии не видны на странице, поэтому это идеальный разделитель.

 {% set break_array = post.blocks.content|split('<!-- more -->', 2) %} 

Затем мы отображаем все, что есть в первом индексе break_array . raw фильтр используется для отображения, а не экранирования HTML.

 {{ break_array[0]|raw }} 

Затем мы проверяем, есть ли в break_array более одного элемента, и в этом случае мы отображаем ссылку для просмотра всего сообщения.

 {% if break_array|length > 1 %} <p> <a href="{{ site.url }}{{ post.url }}"> Read more </a> </p> {% endif %} 

Теперь, когда мы выяснили, как отображается страница индекса, давайте вернемся к _layouts/default.html . Для основной таблицы стилей мы используем Picnic.css . Мы загружаем его из CDN, но вы также можете загрузить файлы в каталог source/css и дать ссылку на них.

 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/picnic/4.1.2/picnic.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/picnic/4.1.2/plugins.min.css"> 

Как разработчики, у нас в основном есть фрагменты кода в наших сообщениях в блоге, поэтому мы связываем соляризованную темную тему для highlight.js, чтобы добавить подсветку синтаксиса.

 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.4.0/styles/solarized-dark.min.css"> 

Наконец, у нас есть собственная таблица стилей, сохраненная в source/css .

 <link href="{{ site.url }}/css/style.css" rel="stylesheet" type="text/css" /> 

Вот содержимое файла style.css :

 #wrapper { width: 900px; margin: 0 auto; } nav .menu { float: left; margin-left: 20px; } nav .menu a { margin-left: 20px; } main { margin-top: 80px; } article { margin-top: 50px; border-bottom: 1px dashed #ccc; color: #1D1D1D; } footer { margin: 20px 0; text-align: center; color: #525252; } .right { float: right; } .left { float: left; } .nav-container { overflow: auto; padding: 30px 0; } article h2 { margin-bottom: 0; padding-bottom: 0; } .date { color: #565656; font-size: 15px; } pre { background: none; } @media screen and (max-width: 780px){ #wrapper { width: 100%; padding: 10px; } } 

Прямо под ссылкой на пользовательскую таблицу стилей ( css/style.css ) у нас есть скрипт highlight.js и код инициализации, который будет применять подсветку синтаксиса.

 <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.4.0/highlight.min.js"></script> <script>hljs.initHighlightingOnLoad();</script> 

Индивидуальная почтовая страница

Теперь, когда мы закончили с индексной страницей, пришло время перейти к шаблону для отдельных страниц постов. Рассматриваемый файл — _views/post.html . Давайте добавим следующее:

 {% extends "default" %} {% block content_wrapper %} <article> <header> <h2>{{ page.title }}</h2> <span class="date">{{ post.date|date('M dS, Y') }}</span> </header> <div class="contents"> {{ page.blocks.content|raw }} </div> {% if page.tags %} <p class="tags"> Tags: {% for tag in page.tags %} <a href="{{ site.url }}/blog/tags/{{ tag|url_encode(true) }}">{{ tag }}</a>{% if not loop.last %}, {% endif %} {% endfor %} </p> {% endif %} </article> {% endblock %} 

В основном это основано на отдельном шаблоне поста из Sculpin Blog Skeleton, с добавленной датой, удаленными категориями и ссылками на страницы.

Возможно, вы заметили, что мы используем другой синтаксис для расширения шаблона по умолчанию:

 {% extends "default" %} 

Это потому, что post считается макетом. В Sculpin все, что вы помещаете в эти каталоги: _views , _layouts , _partials , includes в себя макет. Это означает, что любой шаблон может наследоваться от этих файлов или включать частичные.

Мы упомянули content_wrapper ; блок, который мы пытаемся визуализировать в _layout/default.html . Внутри это блок content :

 <main> {% block content_wrapper %} {% block content %}{% endblock %} {% endblock %} </main> 

Мы уже знаем, что блок content не нужно явно определять, поскольку Twig предполагает, что все, что находится ниже метаблока, является контентом. Это означает, что на самом деле мы можем иметь только следующий код в _views/post.html и публикация будет по-прежнему отображаться:

 {% extends "default" %} 

Но при этом будет отображаться только содержание сообщения, поэтому заголовок, дата и любая другая информация о публикации, которую мы хотим включить, не будут.

Однако, если мы попытаемся переопределить содержимое блока content следующим образом:

 {% extends "default" %} {% block content %} <h1>I'm trying to override the content!</h1> {% endblock %} 

… мы все равно увидели бы содержание поста.

Это означает, что мы не можем переопределить то, что находится внутри блока content . Это где блок content_wrapper вступает в игру. Определив этот блок, мы можем переопределить блок content . Это связано с тем, что блок content_wrapper оборачивает блок content . Теперь мы можем сделать что-то вроде вывода названия и даты публикации.

 {% block content_wrapper %} <article> <header> <h2>{{ page.title }}</h2> <span class="date">{{ post.date|date('M dS, Y') }}</span> </header> <div class="contents"> {{ page.blocks.content|raw }} </div> {% if page.tags %} <p class="tags"> Tags: {% for tag in page.tags %} <a href="{{ site.url }}/blog/tags/{{ tag|url_encode(true) }}">{{ tag }}</a>{% if not loop.last %}, {% endif %} {% endfor %} </p> {% endif %} </article> {% endblock %} 

Добавление Disqus

Добавить Disqus для обработки комментариев очень просто, поскольку он встроен в скелет:

 {% if site.disqus.shortname and site.disqus.shortname != '' %} <div id="disqus_thread"></div> <script type="text/javascript"> /* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */ var disqus_shortname = '{{site.disqus.shortname}}'; // required: replace example with your forum shortname {% if page.disqus.identifier %}var disqus_identifier = '{{page.disqus.identifier}}'; {% endif %} {% if page.disqus.title %}var disqus_title = '{{page.disqus.title}}';{% endif %} {% if page.disqus.url %}var disqus_url = '{{page.disqus.url}}';{% endif %} {% if page.disqus.category_id %}var disqus_category_id = '{{page.disqus.category_id}}';{% endif %} /* * * DON'T EDIT BELOW THIS LINE * * */ (function () { var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); })(); </script> <noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript" rel="nofollow">comments powered by Disqus.</a> </noscript> {% endif %} 

Чтобы это работало, нам нужно обновить файл app/config/sculpin_site.yml с коротким именем Disqus:

 # Insert your disqus shortname disqus: shortname: YOUR_DISQUS_USERNAME 

Архив блога

Как и каждый блог, нашему блогу также нужна страница архива, где читатели могут увидеть все ранее опубликованные посты. В blog.html добавьте следующее:

 --- layout: default title: Posts Archive generator: pagination use: - posts --- {% set year = '0' %} <h2>Posts Archive</h2> {% for post in page.pagination.items %} {% set this_year %}{{ post.date | date("Y") }}{% endset %} {% if year != this_year %} {% set month = '0' %} {% set year = this_year %} {% endif %} {% set this_month %}{{ post.date | date("F") }}{% endset %} {% if month != this_month %} {% set month = this_month %} <h3>{{ month }} {{ year }}</h3> {% endif %} <div> <a href="{{ site.url }}{{ post.url }}">{{ post.title }}</a> </div> {% endfor %} <div> {% if page.pagination.previous_page or page.pagination.next_page %} <div class="nav-container"> {% if page.pagination.previous_page %} <a class="previous" href="{{ site.url }}{{ page.pagination.previous_page.url }}" title="Previous Page"><span class="title">Previous Page</span></a> {% endif %} {% if page.pagination.next_page %} <a class="next" href="{{ site.url }}{{ page.pagination.next_page.url }}" title="Next Page"><span class="title">Next Page</span></a> {% endif %} </div> {% endif %} </div> 

Мы снова используем знакомый синтаксис. Только на этот раз мы не указали, сколько элементов мы хотим на странице. Sculpin будет использовать по умолчанию: 10 предметов.

 --- layout: default title: Posts Archive generator: pagination use: - posts --- 

Далее мы разбиваем следующий код:

 {% for post in page.pagination.items %} {% set this_year %}{{ post.date | date("Y") }}{% endset %} {% if year != this_year %} {% set month = '0' %} {% set year = this_year %} {% endif %} {% set this_month %}{{ post.date | date("F") }}{% endset %} {% if month != this_month %} {% set month = this_month %} <h3>{{ month }} {{ year }}</h3> {% endif %} <div> <a href="{{ site.url }}{{ post.url }}">{{ post.title }}</a> </div> {% endfor %} 

Код выше делает то же самое, что мы делали ранее в файле index.html . То есть он просматривает все элементы на текущей странице, только на этот раз мы группируем каждое сообщение по месяцам и годам.

Развертывание на страницах Github

Для развертывания на страницах Github вам понадобится учетная запись Github, так что заходите и уже не регистрировались. Затем настройте Git , сгенерируйте ключ SSH и добавьте ключ SSH в свою учетную запись Github . Если вы новичок во всем этом, я рекомендую проверить статью Шаумика о Git для начинающих .

После настройки учетной записи Github создайте новый репозиторий , используя следующий формат имени:

 your_github_username.github.io 

Если ваше имя пользователя — капитан, тогда имя репозитория должно быть captain.github.io . Это в значительной степени стандарт для страниц Github, что также означает, что вы можете создать только одну страницу Github для каждой учетной записи.

Затем перейдите в корневой каталог вашего блога и выполните следующее:

 sculpin generate --env prod 

Это создаст сайт для производства. Сгенерированный сайт будет сохранен в каталоге output_prod . После создания вы можете удалить каталог .git . Если вы помните ранее, мы клонировали Скелет Sculpin Blog. На самом деле мы не собираемся вносить код в хранилище, поэтому мы можем удалить его локальное хранилище.

Теперь нам нужно инициализировать новый репозиторий git внутри папки output_prod . Перейдите в каталог и выполните git init . Затем выполните следующее, чтобы зафиксировать изменения:

 git add -A git commit -m "create new blog" 

Добавьте репозиторий Github, который вы создали ранее, как удаленный:

 git remote add origin git@github.com:your_github_username/your_github_username.github.io.git 

Наконец, нажмите на хранилище:

 git push origin master 

Подождите несколько минут, прежде чем ваш блог Sculpin станет доступен по адресу http://your_github_username.github.io . Congrats!

Вывод

В этом уроке мы рассмотрели Sculpin, генератор статических сайтов на основе PHP. Как вы уже видели, Sculpin — действительно хороший вариант для создания статических сайтов. Вы можете получить доступ к коду, используемому в этом руководстве, в этом репозитории Github .

Вы делали какие-либо обширные настройки в своем блоге Sculpin? Есть ли какое-нибудь другое решение статического генератора сайтов, которое вы бы предпочли? Дайте нам знать!