PageBlocks. Интеграция шаблона Snow на MODX

Всем привет.

Данным уроком хочу показать как можно легко и быстро интегрировать шаблон на MODX, надеюсь будет полезно. Забегая наперед, у меня получилось интегрировать сайт за 50 минут.

Для урока выбрал бесплатный шаблон Snow, он включает в себя главную страницу, 2 страницы портфолио и 2 страницы блога. А основным компонентом для интеграции будет PageBlocks.





Шаг 1. Подготовка


1. Устанавливаем MODx
2. Переименовываем файл ht.access на .htaccess
3. Устанавливаем компоненты:
4. Системные настройки:
  • Включаем дружеские url (friendly_urls)
  • Включаем псевдоним родителя в построении uri (use_alias_path)
  • Включаем Fenom на страницах (pdotools_fenom_parser)
  • Включаем Fenom в чанках (pdotools_fenom_default)
  • Разрешаем доступ к объектам MODX и PdoTools (pdotools_fenom_modx)
  • Отключение доступа к странице по id (request_method_strict = Да)
5. Загружаем все статические файлы (стили, скрипты, шрифты и картинки) на сервер в папку assets.

Шаг 2. Создаем страницы



Страницы About и Contact — это ссылки на соответствующие блоки на главной странице.

Шаг 3. Настройка шаблона


Заменяем код главного шаблона на
<!DOCTYPE html>
<html lang="en">
<head>
    {block 'head'}
        {insert 'head'}
    {/block}
</head>
<body>
    {insert 'header'}
    {insert 'navbar-mobile'}
    
    <div class="nk-main">
        {block 'blocks'}
            {'!PageBlocks' | snippet}
        {/block}
        {block 'content'}
            {$_modx->resource.content}
        {/block}
        {insert 'footer'}
    </div>
    
    <script src="/assets/js/combined.js"></script>
</body>
</html>

Создаем чанки:
head:
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">

<title>{$_modx->resource.pagetitle} | {'site_name' | config}</title>
<meta name="description" content="{$_modx->resource.description}">
<link rel="icon" type="image/png" href="/assets/images/favicon.png">
<meta name="viewport" content="width=device-width, initial-scale=1">

<!-- Google Fonts -->
<link href="https://fonts.googleapis.com/css?family=Playfair+Display:400,400i,700,700i%7cWork+Sans:400,500,700" rel="stylesheet" type="text/css">

<link rel="stylesheet" href="/assets/css/combined.css">

header:
<header class="nk-header{if $_modx->resource.id != 1} nk-header-opaque{/if}">
    <nav class="nk-navbar nk-navbar-top{if $_modx->resource.id == 1} nk-navbar-sticky nk-navbar-transparent nk-navbar-white-text-on-top{/if}">
        <div class="container">
            <div class="nk-nav-table">
                <a href="/" class="nk-nav-logo">
                    <img src="/assets/images/logo-light.svg" alt="" width="85" class="nk-nav-logo-onscroll">
                    <img src="/assets/images/logo.svg" alt="" width="85">
                </a>
                
                {'!pdoMenu' | snippet: [
                    'parents' => 0,
                    'tplOuter' => '@INLINE <ul {$classes} data-nav-mobile="#nk-nav-mobile">{$wrapper}</ul>',
                    'outerClass' => 'nk-nav nk-nav-right hidden-md-down',
                    'scheme' => 'full',
                ]}

                <ul class="nk-nav nk-nav-right nk-nav-icons">
                    <li class="single-icon hidden-lg-up">
                        <a href="#" class="nk-navbar-full-toggle">
                            <span class="nk-icon-burger">
                                <span class="nk-t-1"></span>
                                <span class="nk-t-2"></span>
                                <span class="nk-t-3"></span>
                            </span>
                        </a>
                    </li>
                </ul>
            </div>
        </div>
    </nav>
</header>

footer:
<footer class="nk-footer">
    <div class="nk-footer-cont">
        <div class="container text-xs-center">
            <div class="nk-footer-social">
                {'!SocialNetworks' | snippet: [
                    'fontawesome' => 'none',
                    'outerClass' => '',
                    'tpl' => '@INLINE <li><a href="{$link}" target="_blank" title="{$name}"><i class="fa fa-{$icon}"></i></a></li>',
                ]}
            </div>
            <div class="nk-footer-text">
                <p>{'' | date: 'Y'} © {'site_name' | config}</p>
            </div>
        </div>
    </div>
</footer>

Шаг 4. Создаем блоки



1. Блок Hero



Структура блока:
  • фон блока (bg)
  • подзаголовок (subtitle)
  • заголовок (title)
  • заголовок с курсивным начертанием (title_em)
Приступаем к созданию блока в конструкторе блоков PageBlocks, там же в соответствующей вкладке создаем чанк:


Добавляем блок на страницу:


Поздравляю! Мы только что создали первый блок, надеюсь все было понятно. Идем дальше.

2. Блок About


Здесь, все очень просто создаем блок с 2 полями: заголовок и контент, поэтому детально разбирать его не будем.

3. Блок Features



Структура:
  • фон (bg)
  • таблица преимущества (features)
Таблица features будет состоять из полей:
  • Иконка (icon)
  • Число (num)
  • Заголовок (title)
Создаем таблицу и блок:


4. Блок Portfolio



Структура:
  • Заголовок (title)
  • Описание (description)
  • Работы (будет выводиться коллекция)
Каждая работа — это отдельная страница, поэтому будем использовать коллекцию с привязкой к ресурсу.
Коллекция — это создание однотипных блоков. Если блок имеет поля pagetitle, template и alias, то он будет привязан к ресурсу, который будет создан после создания блока. И все совпадающие поля синхронизируются в обоих направлениях. То есть, изменили поле в блоке и в ресурсе соответствующее поле тоже будет изменено и наоборот.

Шаг 1. Создаем блок для коллекции:
Поля:
  • Превью (img)
  • Заголовок (pagetitle)
  • Тег (tag)
  • Шаблон (template)
  • Псевдоним (alias)


Чанк work:
<div class="nk-isotope-item" data-filter="{$tag}">
    <div class="nk-portfolio-item nk-portfolio-item-square nk-portfolio-item-info-style-1">
        <a href="{$uri}" class="nk-portfolio-item-link"></a>
        <div class="nk-portfolio-item-image">
            <div style="background-image: url(/{$img});"></div>
        </div>
        <div class="nk-portfolio-item-info nk-portfolio-item-info-center text-xs-center">
            <div>
                <h2 class="portfolio-item-title h3">{$pagetitle}</h2>
                <div class="portfolio-item-category">{$tag}</div>
            </div>
        </div>
    </div>
</div>

Шаг 2. Создаем коллекцию для наших работ.
Показывать коллекцию будем только в ресурсе c id 4 (Portfolio).


Шаг 3. Заполняем коллекцию.


Шаг 4. Создаем блок, в котором будем выводить нашу коллекцию.
Если вернуться в самое начало к блоку Portfolio, то нам нужны 2 поля — заголовок и описание.


Чанк projects
<div class="nk-box bg-white" id="projects">
    <div class="nk-gap-4 mt-5"></div>
    
    <h2 class="text-xs-center display-4">{$title}</h2>
    
    <div class="nk-gap mnt-6"></div>
    {if $content}
    <div class="container">
        <div class="row">
            <div class="col-lg-8 offset-lg-2">
                <div class="text-xs-center">{$content}
                </div>
            </div>
        </div>
    </div>
    {/if}

    <div class="nk-gap-2 mt-12"></div>
    <div class="container">
    <div class="nk-portfolio-list nk-isotope nk-isotope-3-cols">

        {'!PageBlocks' | snippet: [
            'rid' => 4,
            'collection' => 2,
            'limit' => 9
        ]}

    </div>
    </div>
    <div class="nk-gap-4 mt-15"></div>
</div>

5. Блок Reviews



Здесь делаем по аналогии с блоком Features
Шаг 1. Создаем таблицу с полями: content и author
Шаг 2. Создаем блок в полями bg и reviews (ранее созданная таблица)

Чанк reviews:
<div class="nk-box bg-dark-1">
    <div class="bg-image bg-image-parallax" style="background-image: url({$bg});"></div>
    <div class="nk-gap-5 mnt-6"></div>
    <div class="nk-gap-3"></div>
    <div class="container-fluid">
        <!-- START: Carousel -->
        <div class="nk-carousel nk-carousel-all-visible text-white" data-autoplay="18000" data-dots="true">
            <div class="nk-carousel-inner">
                {foreach $reviews as $review}
                <div>
                    <div>
                        <blockquote class="nk-blockquote-style-1 text-white">
                            {$review.content}
                            <cite>{$review.author}</cite>
                        </blockquote>
                        <div class="nk-gap-3 mt-10"></div>
                    </div>
                </div>
                {/foreach}
            </div>
        </div>
        <!-- END: Carousel -->
    </div>
    <div class="nk-gap-4 mt-3"></div>
</div>

6. Блок Partners



Блок состоит только из логотипов, поэтому создаем блок с одним полем — галерея.


Чанк partners:
<div class="bg-white">
    <div class="container">
        <div class="nk-carousel-2 nk-carousel-x4 nk-carousel-no-margin nk-carousel-all-visible">
            <div class="nk-carousel-inner">
                {foreach $logos as $logo}
                <div>
                    <div>
                        <div class="nk-box-1">
                            <img src="{$logo.url}" alt="{$logo.title}" class="nk-img-fit">
                        </div>
                    </div>
                </div>
                {/foreach}
            </div>
        </div>
    </div>
</div>

Добавляем логотипы:


7. Блок Latest blog



Данный блог аналогичен блоку Portfolio. В итоге должно получиться так:


Чанк latest-blog
<div class="nk-box bg-gray-1" id="blog">
    <div class="nk-gap-4 mt-5"></div>
    <h2 class="text-xs-center display-4">{$title}</h2>
    <div class="nk-gap mnt-6"></div>
    
    {if $content}
    <div class="container">
        <div class="row">
            <div class="col-lg-8 offset-lg-2">
                <div class="text-xs-center">{$content}
                </div>
            </div>
        </div>
    </div>
    {/if}

    <div class="nk-gap-2 mt-12"></div>
    <div class="container">
        <!-- START: Carousel -->
        <div class="nk-carousel-2 nk-carousel-x2 nk-carousel-no-margin nk-carousel-all-visible nk-blog-isotope" data-dots="true">
            <div class="nk-carousel-inner">
                
                {set $blogs = '!PageBlocks' | snippet: [
                    'rid' => 5,
                    'collection' => 3,
                    'limit' => 9,
                    'return' => 'json'
                ] | fromJSON}
                
                {foreach $blogs as $blog}
                <div>
                    <div>
                        <div class="pl-15 pr-15">
                            {$_modx->getChunk('blog', $blog)}
                        </div>
                        <div class="nk-gap-1"></div>
                    </div>
                </div>
                {/foreach}

            </div>
        </div>
        <!-- END: Carousel -->
    </div>
    <div class="nk-gap-5 mt-20"></div>
</div>

Чанк blog:
<div class="nk-blog-post">
    <div class="nk-post-thumb">
        <a href="/{$uri}">
            <img src="/{$img}" alt="{$pagetitle|notags}" class="nk-img-stretch">
        </a>
        <div class="nk-post-category"><a href="/{$uri}">{$tag}</a></div>
    </div>
    <h2 class="nk-post-title h4"><a href="blog-single.html">{$pagetitle}</a></h2>

    <div class="nk-post-date">
        {$publishedon | date_format: '%d.%m.%Y'}
    </div>

    <div class="nk-post-text">
        <p>{$description}</p>
    </div>
</div>

8. Блок Contacts



Структура:
  • Заголовок (title)
  • Описание (content)
  • Адрес (address)
  • Телефон (phone)
  • Почта (email)
  • Факс (fax)


Чанк contacts:
<div class="container" id="contact">
    <div class="nk-gap-5"></div>
    <div class="row vertical-gap">
        <div class="col-lg-5">
            
            <h2 class="display-4">{$title}</h2>
            <div class="nk-gap mnt-3"></div>

            {$content}

            <ul class="nk-contact-info">
                {if $address}
                    <li><strong>Address:</strong> {$address}</li>
                {/if}
                
                {if $phone}
                    <li><strong>Phone:</strong> {$phone}</li>
                {/if}
                
                {if $email}
                    <li><strong>Email:</strong> {$email}</li>
                {/if}
                
                {if $fax}
                    <li><strong>Fax:</strong> {$fax}</li>
                {/if}
            </ul>
            
        </div>
        <div class="col-lg-7">
            
            {'!AjaxForm' | snippet: [
                'form' => 'form.contact',
                'hooks' => 'email',
                'emailTpl' => 'email.contact',
                'emailSubject' => 'Contacts',
                'emailTo' => 'Superboshnik@gmail.com',
                'emailFrom' => $_modx->config.emailsender,
                'emailFromName' => $_modx->config.site_name,
                'validate' => 'name:required,email:required,title:required,message:required',
                'validationErrorMessage' => 'В форме содержатся ошибки!',
                'successMessage' => 'Сообщение успешно отправлено',
            ]}
            
        </div>
    </div>
    <div class="nk-gap-5"></div>
</div>

Чанк form.contact
<form action="{$_modx->resource.uri}"  method="post" class="nk-form nk-form-ajax">
    <div class="row vertical-gap">
        <div class="col-md-6">
            <input type="text" class="form-control" name="name" placeholder="Your Name">
        </div>
        <div class="col-md-6">
            <input type="email" class="form-control" name="email" placeholder="Your Email">
        </div>
    </div>

    <div class="nk-gap-1"></div>
    <input type="text" class="form-control" name="title" placeholder="Your Title">

    <div class="nk-gap-1"></div>
    <textarea class="form-control" name="message" rows="8" placeholder="Your Comment" aria-required="true"></textarea>
    <div class="nk-gap-1"></div>
    
    <button class="nk-btn" type="submit">Send Message</button>
</form>

Чанк email.contact:
<ul>
    <li><b>Name:</b> {$name}</li>
    <li><b>Email:</b> {$email}</li>
    <li><b>Title:</b> {$title}</li>
    <li><b>Message:</b> {$message}</li>
</ul>

Шаг 5. Создаем другие страницы



Страница Portfolio

На этой странице нам нужно вывести 9 последних работ из коллекции. Для вывода будем использовать сниппет pdoPage. В настройках ресурса отключаем html-редактор и вставляем код в поле content:
<div id="pdopage">
    <div class="container">
        <!-- START: Filter -->
        <div class="nk-pagination nk-pagination-nobg nk-pagination-center">
            <a href="#nk-toggle-filter">
                <span class="nk-icon-squares"></span>
            </a>
        </div>
        <ul class="nk-isotope-filter">
            <li class="active" data-filter="*">All</li>
            <li data-filter="Branding">Branding</li>
            <li data-filter="Print">Print</li>
            <li data-filter="Photography">Photography</li>
            <li data-filter="Design">Design</li>
            <li data-filter="Mockup">Mockup</li>
        </ul>
        <!-- END: Filter -->
    
        <div class="nk-portfolio-list nk-isotope nk-isotope-3-cols rows">
            
            {'!pdoPage' | snippet: [
                'element' => 'PageBlocks',
                'sortby' => 'rank',
                'sortdir' => 'desc',
                'limit' => 9,
                'ajaxMode' => 'button',
                'ajaxTplMore' => '@INLINE <div class="nk-gap-4"></div><div class="nk-pagination nk-pagination-center btn-more"><a href="#">Load More Works</a></div>',
                'where' => [
                    'resource_id' => $_modx->resource.id,
                    'collection_id' => 2,
                    'active' => 1,
                ]
            ]}
            
        </div>
        <div class="nk-gap-4"></div>
    </div>
    {'page.nav' | placeholder}
</div>

Внутренняя страница работы

Добавляем блок:


Чанк portfolio-single
<div class="container">
    <div class="nk-portfolio-single">

        <div class="nk-gap-4 mb-14"></div>
        <h1 class="nk-portfolio-title display-4">{$title ?: $_modx->resource.pagetitle}</h1>
        <div class="row vertical-gap">
            <div class="col-lg-8">
                <div class="nk-portfolio-info">
                    <div class="nk-portfolio-text">
                        {$content}
                    </div>
                </div>
            </div>
            <div class="col-lg-4">
                <table class="nk-portfolio-details">
                    {if $client}
                    <tr>
                        <td>
                            <strong>Client:</strong>
                        </td>
                        <td>{$client}</td>
                    </tr>
                    {/if}
                    {if $date}
                    <tr>
                        <td>
                            <strong>Date:</strong>
                        </td>
                        <td>{$date | date_format: '%d.%m.%Y'}</td>
                    </tr>
                    {/if}
                </table>
            </div>
        </div>
        <div class="nk-gap-4 mt-14"></div>

    </div>
</div>

{if $img}
    <img class="nk-img-fit" src="/{$img}">
{/if}

<div class="nk-pagination nk-pagination-center">
    <div class="container">
        
        {'!pdoNeighbors' | snippet: [
            'tplWrapper' => '@INLINE {$prev}<a class="nk-pagination-center" href="#"><span class="nk-icon-squares"></span></a>{$next}',
            'tplPrev' => '@INLINE <a class="nk-pagination-prev" href="/{$uri}"><span class="pe-7s-angle-left"></span> Previous Work</a>',
            'tplNext' => '@INLINE <a class="nk-pagination-next" href="/{$uri}">Next Work <span class="pe-7s-angle-right"></span></a>',
        ]}
    </div>
</div>

Страница Blog и внутренняя страница

Делаем по аналогии с портфолио.

Чанк blog-single
{if $img}
<div class="nk-header-title nk-header-title-lg">
    <div class="bg-image">
        <div style="background-image: url(/{$img});"></div>
    </div>
    <div class="nk-header-table">
        <div class="nk-header-table-cell">
            <div class="container">
            </div>
        </div>
    </div>
</div>
{/if}

// Здесь стоит остановится на одном моменте. 
// На внутренней страницы блога мы должны вывести тег статьи, но мы не расширяли модель ресурса, 
// поэтому это поле находиться только в блоке и чтобы получить все поля используем такую конструкцию:
{set $block = '!PageBlocks' | snippet: [
    'object_id' => $_modx->resource.id,
    'return' => 'json',
] | fromJSON}

<div class="container">
    <div class="row">
        <div class="col-lg-8 offset-lg-2">
            <div class="nk-gap-4"></div>

            <!-- START: Post -->
            <div class="nk-blog-post nk-blog-post-single">
                <h1 class="display-4">{$title ?: $_modx->resource.pagetitle}</h1>

                <div class="nk-post-meta">
                    <div class="nk-post-date">{$_modx->resource.publishedon}</div>
                    
                    // И теперь у нас есть доступ к полям блока и выводим тег:
                    <div class="nk-post-category"><a href="#"{$block.tag}</a></div>
                </div>

                <!-- START: Post Text -->
                <div class="nk-post-text">
                    {$content}
                </div>
                <!-- END: Post Text -->
            </div>
            <!-- END: Post -->

            <div class="nk-gap-3"></div>
        </div>
    </div>
</div>

<div class="nk-pagination nk-pagination-center">
    <div class="container">
        {'!pdoNeighbors' | snippet: [
            'tplWrapper' => '@INLINE {$prev}<a class="nk-pagination-center" href="#"><span class="nk-icon-squares"></span></a>{$next}',
            'tplPrev' => '@INLINE <a class="nk-pagination-prev" href="/{$uri}"><span class="pe-7s-angle-left"></span> Previous Work</a>',
            'tplNext' => '@INLINE <a class="nk-pagination-next" href="/{$uri}">Next Work <span class="pe-7s-angle-right"></span></a>',
        ]}
    </div>
</div>

Вот и все, сайт готов! Кому понравилось ставьте лайк.
Ссылка на сайт
Aleksandr Huz
20 октября 2021, 10:05
modx.pro
2
2 093
+18
Поблагодарить автора Отправить деньги

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

Николай Савин
20 октября 2021, 10:17
+3
Вот и первый урок в новом разделе «Уроки». Александр Красавчик!
Кстати если кто-то не заметил — у нас созданы два новых раздела.
Уроки и обзоры. Сделано это в рамках задачи по популяризации MODX.
В рамках раздела обзоры — хотим дать больше контента и свежего взгляда на популярные и не очень компоненты, информации о которых не хватает!
В рамках раздела уроки — планируем рассказывать о «современных» технологиях разработки, объясняя почему сниппет IF — который до сих пор встречается, это плохо!
    Фарит
    Фарит
    20 октября 2021, 19:21
    +1
    За такие уроки плюсики должны умножаться на 5
      Николай Савин
      20 октября 2021, 19:39
      +3
      Согласен, материал отличный! Не исключено, что так и сделаем. Но раздел скорее всего будет закрытым и писать в него можно будет только при определенном рейтинге или каком то другом ограничении. Планов много — добраться бы.
      Кстати кому интересно узнать о планах развития MODX.pro?
        Андрей Шевяков
        20 октября 2021, 20:41
        0
        Кстати кому интересно узнать о планах развития MODX.pro?
        Интересно! Ждем анонс.
    Семён Кудрявцев
    21 октября 2021, 22:07
    +3
    Компонент реально крутой получился, уже перерос компонент от Марка из modmore. Единственное чего не хватает, возможности указывать файловые чанки, снова нужно писать код в админке.
      Sergey (Sentinel)
      26 октября 2021, 19:26
      0
      а что нельзя в чанке написать
      {include "file:chunks/chunk.tpl"}
      ??
        Aleksandr Huz
        26 октября 2021, 19:40
        0
        Можно, а смысл? В чанке вызывать файловый чанк. Скоро будет поддержка файловых чанков.
          Sergey (Sentinel)
          26 октября 2021, 20:02
          0
          Т.е. код оставищь в чанке и можно будет указать путь, как у MODX?
            Aleksandr Huz
            26 октября 2021, 20:05
            +1
            Ага, будет создаваться файловых чанк
      Щукин Дмитрий
      21 апреля 2022, 15:48
      0
      Кто-то пробовал делать поиск по сайту на этом компоненте?
        Щукин Дмитрий
        19 августа 2022, 07:30
        0
        Ну сам себе и отвечу, компонент AdvSearch прекрасно ищет по json PageBlocks и MigX
        Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
        11