MODX, как сделать красиво для администратора сайта. Часть 1

Пока нет у нас третьего MODX’а, как завещали нам старшие товарищи, будем популяризовывать вторую версию.
Данный цикл статьей (увы, в одну статью не уложиться, очень много информации) посвящён бэкенду, причём настройке бэкенда для редактора, чтобы человек без особых знаний веба смог стать администратором сайта и при этом, по незнанию, не обрушил фронт. Статья впервую очередь расчитана на новичков MODXa.

Вводные: у вас есть готовая вёрстка, и осталось сделать удобную админку, чтобы редактор не видел ни кода, ни вызовов сниппетов и вводил только текст и загружал изображения. Ну ещё по кнопкам тыкал, как же без этого. Да, настройка ACL (Контроль доступа) тоже будет. В бэкенде установлен pdoTools и в шаблонах подключен Fenom. Кроме этого будем использовать и другие пакеты, по ходу инструкции я буду всё разъяснять. Все действия будут происходить в бэкенде.

Базовый шаблон


Итак, приступим. Переключаем нашу боковую панель на раздел «Элементы». Жмём иконку с папкой (Новая категория) и создаём категории «Base» и «PageSections» (их будет много разных, чтобы не было у нас ни ТВ, ни чанков, ни сниппетов «Без категории»). Открываем наш базовый шаблон, далее «BasePageTemplate», и указываем ему категорию «Base». Чтобы улучшить визуализацию, не забываем прописывать «Значок» (поле сразу после «Описания»). Работают иконки следующим образом. MODX в админке использует Font Awesome версии 4.7, так что идём на сайт Font Awesome выбираем красивую иконку и копируем название, например, для BaseTemplate я выбрал «fa-star-o». Переключаемся обратно на наш шаблон, вставляем название в поле «Значок» и меняем префикс «fa» на «icon», т.е. получаем имя «icon-star-o». Сохраняем и вуаля, у нас уже красивая иконка перед заголовком в дереве ресурсов. И теперь и вам, и администратору будет сразу видно, какая страница какой шаблон использует.


Очень упрощенно, ваша обычная страница выглядит так:

<!DOCTYPE html>
<html lang="{$_modx->config.cultureKey}" class="no-js">
<head>
	{include 'Head'}
</head>
<body>
<header class="header">
	{include 'Navbar'}
</header>
{block 'main'}
<main class="main main_base">
	<div class="container">
		<figure>
			<img src="{'ImagePlus' | snippet : ['tvname'=>'image_1','options'=>'f=jpeg&w=1920']}" alt="{$_modx->resource.pagetitle}">
		</figure>
		<h1>{$_modx->resource.pagetitle}</h1>
		<p>{$_modx->resource.longtitle}</p>
		{block 'content'}
		<div class="main__content">
			{$_modx->resource.content}
		</div>
		{/block} {* /content *}
	</div>
</main>
{/block} {* /main *}
{include 'Footer'}
</body>
</html>

Блоков для расширения шаблонов можете выставить столько, сколько вам будет нужно.
Почему «Image+»? Помимо стандартной загрузки этот пакет позволяет редактору обрезать картинку под текущие нужды. Кроме того, поддержка дополнения из коробки есть в MIGX, так что вещь более чем удобная. Документацию по пакету Image+ можно прочитать здесь. Все доступные опции сниппета указаны здесь. Теперь про организацию загрузки картинок.

Картинки


Для загрузки картинок нам надо организовать для редактора удобный источник файлов. Настроить это можно в разделе «Медиа»->«Источники файлов». После нажатия кнопки «Создать новый источник файлов» вам откроется окно для введения имени и описания источника. Для примера, имя напишем «User», а в описании укажем: «Пользовательские изображения». Заполняя поля «basePath» и «baseUrl» помните, что путь должен заканчиваться слешем, поскольку дальше подставляются относительные пути, например, «assets/images/user/» (каталог должен существовать). Не забываем заполнить поле «allowedFileTypes», чтобы не грузили ничего кроме картинок.



Здесь всё, сохранили и теперь на нашей боковой панели в разделе «Элементы» жмём кнопку создания нового ТВ. Сразу же создаём новую категорию: «Фото».
Имя нового ТВ: image_1 (это имя мы уже используем в базовом шаблоне). Присваиваем нашу новую категорию: Фото. Подпись: Фото №1 (потому как могут быть и №2 и т.д.). Не забываем указать «Порядок сортировки», чтобы не было каши среди наших ТВ. Переключаемся на раздел «Параметры ввода» и выбираем тип ввода «Image+» (пакет должен быть уже установлен) и меняем/заполняем дополнительные поля, которые появились после смены типа (у меня все опции Image+ пустые и показ полей отключен, вы же можете сделать так, как вам удобно). Дальше в разделе «Доступно для шаблонов» отмечаем наш «BasePageTemplate», а в разделе «Источники файлов» для каждого контекста выбираем наш новый источник файлов «User». Сохраняем. Теперь все фотографии будут складываться не в корень сайта, а в отдельный каталог.

Дополнительные секции


Простейший базовый шаблон готов и теперь можно заполнять простые текстовые страницы материалом. Если же на этих страницах должны повторяться какие-либо секции до подвала, то сделать это не сложно, чуть позже я расскажу, как это сделать.

Первая страница


Первая страница сайта по дизайну в 99.99% случаев отличается от остальных, но какая-то часть разметки может быть общая и для остальных страниц. Как сделать, чтобы все секции первой страницы были динамичными, т.е. их можно было бы включать и выключать независимо друг от друга, менять местами и при этом оставалось удобство редактирования контента? Все достаточно просто: роль секций будут играть дочерние ресурсы первой страницы, главное не забыть включить «Скрыть из меню». И если условная верстка первой страницы выглядит вот так:

{extends 'BasePageTemplate'}
{block 'main'}
<main class="main main_index">
	<section class="s-intro">
		<h1>Заголовок</h1>
		<p>Какой-то текст</p>
	</section>
	<section class="s-about">
		<figure>
			<img src="/assets/images/image.jpg" alt="подпись">
		</figure>
		<h1>О нас</h1>
		<p>Какой-то текст</p>
		<a href="#">Смотреть</a>
	</section>
	<section class="s-services">
		<h1>Услуги</h1>
		<p>Вступительный текст</p>
		<ul>
			<li><a href="service-1">Имя услуги 1</a></li>
			<li><a href="service-2">Имя услуги 2</a></li>
			<li><a href="service-2">Имя услуги 3</a></li>
		</ul>
	</section>
</main>
{/block}

То после разделения на секции шаблон первой страницы выглядит до безобразия просто:

{extends 'BasePageTemplate'}
{block 'main'}
<main class="main main_index">
	{'pdoResources' | snippet : [
		'tpl' => 'psEmptyTpl',
		'tplCondition' => 'template',
		'conditionalTpls' => '{ "3":"sIntroTpl", "4":"sAboutTpl", "5":"sServicesTpl" }',
		'includeTVs' => 'image_1,resource_id',
		'sortby' => ["menuindex"=>"ASC"],
		'depth' => 0,
	]}
</main>
{/block}

Динамику секций нам обеспечивает параметр «tplCondition», у нас значение этого параметра: «template», т.е. в зависимости от шаблона страницы будет подгружен нужный чанк. Принцип работы следующий, в параметре «conditionalTpls» в JSON у нас указаны ID шаблона и название чанка, который будет отвечать за внешний вид нашей секции. У нас уже созданы шаблоны «BasePageTemplate» и «FirstPageTemplate», теперь создадим шаблоны для наших секций. Для секции «s-intro» мы создадим шаблон «sIntro», для секции «s-about» — «sAbout», а для секции «s-services» — «sServices» все эти шаблоны заносим в ранее созданную категорию «PageSections». Если других шаблонов кроме базовой и первой страниц у нас не было, то новые темплейты получат ID 3,4 и 5. Что и отображено в нашей выборке. Пример кода шаблона секции:

{$_modx->sendRedirect($_modx->resource.parent | url)}

Т.е. если вдруг кто-то случайно зайдёт на такую страницу, то сразу будет переадресован на родителя. При желании можно поменять переадресацию на первую страницу сайта:

{$_modx->sendRedirect($_modx->config.site_start | url)}

Теперь пришло время создать чанки для рендера наших секций. Имена уже указаны в шаблоне, единственное, не забывайте про категории. У нас уже есть категория «PageSections», в неё и добавляйте новые секции. Например, для секции «s-about» мы создаём чанк с именем «sAboutTpl» и содержание у него будет следующим:

<section class="s-about">
	<figure>
		<img src="{'ImagePlus' | snippet : ['docid'=>$id,'tvname'=>'image_1', 'options'=>'f=jpeg&w=1920']}" alt="{$pagetitle}">
	</figure>
	<h2>{$pagetitle}</h2>
	<p>{$introtext}</p>
	{if $resource_id}
	<a href="{$resource_id | url}">{'view' | lexicon}</a>
	{/if}
</section>

Аналогично поступаем с остальными чанками секций. Теперь у нас есть связка между секциями нашей вёрстки и потомками первой страницы.
Если вы используете экстру SEO Tab, то для того, чтобы наши секции случайно не всплыли в файде robots.txt, то просто добавьте опцию &templates=`-3,-4,-5` для вызова StercSeoSiteMap. То же самое должно сработать и для pdoSitemap. Теперь и овцы целы и волки сыты.
Для связи секции с более полными версиями страниц, например, чтобы кнопка из секции «Коротко о нас» вела на страницу «О нас» мы будем использовать ТВ «resource_id». Настройки для TV очень просты: «Параметры ввода»:

  • Тип ввода: число.
  • Разрешить десятичные: нет.
  • Разрешить минус: нет.
Теперь у нас есть поле, которое будет содержать ИД основной страницы. Не забываем о создании категории для этой ТВ, чтобы всё было разложено по полочкам: «Ссылки».

Динамичные псевдонимы


Есть ещё один момент: очень часто редактора при создании новых документов начисто забывают про псевдонимы. Частично решить эту проблему нам может помочь плагин для динамичного создания псевдонима:

// OnDocFormSave
switch ($modx->event->name) {
  case 'OnDocFormSave':
    $template_value = $resource->get('template');
    switch ($template_value) {
        case 5:
        case 4:
        case 3:
            $custom_alias = $resource->getOne('Parent')->get('alias').'-'.$resource->get('id');
			if ($custom_alias) {
				$resource->set('alias',$resource->cleanAlias($custom_alias));
				$resource->save();
			} else {
				$modx->log(modX::LOG_LEVEL_INFO, 'Невозможно получить родителя для ресурса: ' . $resource->get('id'));
			}	
            break;
    }
    break;
};

Событие, на которое должен срабатывать плагин: OnDocFormSave. В данном случае ресурсам с ID 3,4,5 будет автоматом будет задан псевдоним, состоящий из псевдонима родителя и ID текущего ресурса. После этого документ будет сохранён.
Почему частично? Не всегда MODX автоматически создаёт псевдоним для страницы, а плагин срабатывает во время сохранения, а проверка на псевдоним происходит до этого. Но в большинстве случаев всё ОК и произойдет автоматическая замена псевдонима.

Остальные страницы


Тут всё гораздо проще, в руководстве по Fenom всё отлично расписано про расширение шаблонов. Единственное, что хочется добавить: если вам нужен повтор какой-то секции с первой или какой-то другой страницы, то в опции parents указываем $_modx->config.site_start или реализуем через уже созданное ТВ «resource_id», и добавляем параметр where. Вариант вывода одной секции:

{'pdoResources' | snippet : [
		'parents' => $_modx->config.site_start,
		'tpl' => 'sIntroTpl',
		'includeTVs' => 'image_1,resource_id',
		'sortby' => ["menuindex"=>"ASC"],
		'depth' => 0,
		'where' => ["template"=>"3"],
		'limit' => 1
	]}

Вариант вывода нескольких секций подряд:

{'pdoResources' | snippet : [
		'parents' => $_modx->config.site_start,
		'tpl' => 'psEmptyTpl',
		'tplCondition' => 'template',
		'conditionalTpls' => '{"4":"sAboutTpl", "5":"sServicesTpl" }',
		'includeTVs' => 'image_1,resource_id',
		'sortby' => ["menuindex"=>"ASC"],
		'depth' => 0,
		'where' => ["template:IN"=>"4,5"],
		'limit' => 2
	]}

Поскольку параметр where принимает массивы, то все правила, описанные здесь прекрасно работают.
На этом пока всё, если что не ясно, задавайте вопросы. Отвечу или здесь или во второй части.
Янис
08 октября 2019, 10:01
modx.pro
4
2 936
+4

Комментарии: 10

Николай Савин
08 октября 2019, 13:03
+6
Я вот честно говоря не вчитываясь в простыню текста не особо понял о чем статья и какое отношение к удобной админке имеет верстка шаблона. Вернее судя по заголовку статья про удобную и красивую админку, а в тексте ни одного примера, и сплошь разговоров про шаблонизацию.
Навскидку может в начале какую картинку разместишь — как итог, покажешь к чему ты ведешь?
    Янис
    08 октября 2019, 13:30
    0
    Идея в том, что администранор не видит код вообще. Чтобы это работало, для начала всё выносим из контента в шаблоны / чанки, а затем через ACL (в начале статьи упомянул, но похоже невнятно) делаем редакторскую группу, чтобы участники этой группы, видели только то, что им положено.
      Михаил
      08 октября 2019, 14:16
      +1
      а просто вопрос, кто то делает код в контенте?
        Янис
        08 октября 2019, 14:22
        0
        Увы, встречаются такие персонажи. Поэтому и захотелось написать инструкцию для новичков, как разнести текст и код.
          Alexander V
          08 октября 2019, 18:12
          +1
          Делают конечно. Это касается разношерстных статических страниц.
        Дмитрий
        10 октября 2019, 16:10
        0
        Человек, по всей видимости, пытался описать какие-то «best practices» по сборке сайта. Тема нужная, инициатива похвальная, но стоит лучше структурировать информацию, мне кажется, если взялся за это.
        iWatchYouFromAfar
        08 октября 2019, 13:25
        +1
        Ну да, обычная натяжка верстки на MODx с помощью Фенома. Причем тут удобная / красивая админка не очень понятно. А вот для тех, кто учится натягивать верстку с помощью Фенома, мастхев.

        P.S.
        Еще юзайте ClientConfig — удобная вещь.
          Михаил
          08 октября 2019, 13:39
          +2
          switch ($template_value) {
                  case 5:
                  case 4:
                  case 3:
          читабельнее и удобнее так:
          case 5,4,3.....:
            Егор
            10 октября 2019, 08:44
            0
            настройке бэкенда для редактора, чтобы человек без особых знаний веба смог стать администратором сайта и при этом, по незнанию, не обрушил фронт. Статья впервую очередь расчитана на новичков MODXa.
            Ограничение доступа для менеджера сайта разве это уже не реализовано?
              Янис
              10 октября 2019, 10:48
              +1
              Можно купить, а можно самому настроить. Скоро вторая статья будет, как всё это организовать
              Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
              10