Восьмибитный путь MODx revo,
часть 5:
программирование не стандартного функционала сайта или MIGX во всем его дьявольском великолепии

Восьмибитный путь MODx
или

Сайт с нуля MODx revo, часть 5:
программирование не стандартного функционала сайта
или MIGX во всем его дьявольском великолепии

Разделяй и властвуй с латиницей
в названиях элементов админки

Использование латиницы, позволит избежать ошибок, при использовании компонентов, работающих с дополнительными полями, называемых в modx Template Variables (TV) — переменные шаблона. Для начала, преобразуй основные элементы, вот как:

Властвуй над латиницей

Судя по всему — названия элементов, видать-именовать вольно аж на китайском и даже на балабольском! Вероятно, это каким-то таинственным образом, связано с совершенной архитектурой веб-приложения для разработки высокопрофессиональных сайтов modX, хотя, останови свои мысли об этом.

можно делать хоть на китайском
Чтоо за чепуха?!
РОЙ БИДРИЛЛ!?!!

Однако, корректно работать с любыми компонентами — будет именно латиница. И все же когда, предо мной мелькнет задача с поиском и заменой названий: я буду знать как сообразить это сверх-стремительно и пластично; гляди — названия элементов они отрегулировали, сейчас — нужно поменять имена в шаблонах и чанках сайта (для того, чтобы все сниппеты верно работали и сайт корректно собирался). На тестовом сайте там чуток кода, а случаются сайты — где элементов, да сниппетов немалое множество. Так вот: скачай и установи компонент modDevTools. Начни замену с чанков:

Дополнительные поля (TV):

Основной шаблон:

moddevtools

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

Перемести основной шаблон сайта в чанк

Скопируй содержимое файла /assets/tpl/base.tpl в новый чанк tpl (сокращение от template), категория Шаблоны. Пробегись по шаблонам сайта, где используется статический файл, убери соответствующую галочку и замени код шаблона на: [[$tpl]]

Перенеси основной шаблон сайта в чанк

Чем же так хорош MIGX?
function apokaliptus(e){ return true; } # ?!

В предыдущих публикациях, я уже рассматривал простенькую конфигурацию, для загрузки картинок в ресурс, но воистину, это лишь малая часть возможностей MIGX — на деле, функционал, которым обладает этот компонент — дает головокружительную свободу для программирования абсолютно любого сайта, с невероятными: даже самыми бешенными задумками, MIGX нещадно справляется, не оставляя компромиссов любой иной системе управления, при этом сохраняя простоту, изящность и удобство добавления информации на сайт; освоив этот компонент, ты будешь в восторге от того, как чудовищно просто создаются модули, как элементарно, например, разрабатывается управление лендинг пейджем, корпоративным и любым другим сайтом, вне зависимости от его масштаба да функционала; и вот, в сверкающем блеске MIGX, ты, наконец-то обретешь покой, и, вероятно, придет понимание того — сколь скудны, приземлены и как меркнут тысячи, триллионы строк кода альтернативных систем управления, фреймворков, и прочих, прочих: преклоняясь пред гениальностью MIGX и modx revolution, программы богов; сейчас, остынь слегка, будет сложно, экстремально сложно, но оно того стоит!
(З.Ы.: на самом деле очень легко)

Чем же так хорош MIGX?

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

Полетели ! ! !
//гуси-лебеди//

MIGX — потрясающий компонент,
и не спорь
и не какай принцесса

MIGX — это тип дополнительного поля, для объединения нескольких дополнительных полей в одном поле. Это объединение: значительно упрощает рабочий процесс для итоговых пользователей — менеджеров, дабы они смогли добавлять сложные элементы данных на сайт, с присущей modx грацией. Элемент данных может состоять из любого количества любых других дополнительных полей: в том числе текст, изображения, файлы, флажки и так далее...

Реально очаровывает то, что в качестве дополнительного поля, может быть использовано другое поле MIGX, с безграничной степенью вложенности... это кайфовое балансирование: на гранях безумия и гениальности — телепортирует твое сознание на новый уровень бытия, будь уверен!

Ты самостоятельно ознакомишься с документацией, и, разумеется повторишь все туториалы на тестовом сайте. Не останавливайся на достигнутом, и изучай MIGXdb, воспроизведи все туториалы самостоятельно. По MIGXdb — советую урок Пана Евгениуса.

Только выполняя обучающие уроки, ты сможешь творить чудеса, с помощью этого компонента — не пытайся понять его за раз: делай, изучай, читай, делай, мысли шире, в modx revolution и MIGX возможно все. Ах, этот сакральный грааль MIGX на modx revolution, компонент выше бесконечного восхищения и всяческих похвал...

Вернемся на Землю.
На тестовый сайт.

Главная страница: слайдер

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

Элементы → Дополнительные поля → Главная → tpl.1.slider

Правой кнопкой мыши, не спеши, счёлкни по элементу и выбери пункт Редактировать. Перейди на вкладку дополнительного поля Параметры ввода, ликвидируй значение Конфигурации: resourcealbum, а в следующую строку Вкладки формы, копируй этот кусочек кода:

[
{"caption":"Элемент", "fields": [
  {
    "field":"alt",
    "caption":"alt"
  },
  {
    "field":"title",
    "caption":"title"
  },
  {
    "field":"image",
    "caption":"Изображение",
    "inputTVtype":"image",
    "sourceFrom":"migx"
  }
]}
]

Строка 15 "sourceFrom":"migx", означает что: источник файлов для изображения будет подхватываться из настроек TV-поля (вкладка Источники файлов), а там необходимо выбрать источник: Слайдер.

В следующее поле, Разметка колонок, копируй:

[
  {"header": "Изображение", "width": "160", "sortable": "false", "dataIndex": "image","renderer": "this.renderImage"},
  {"header": "alt", "sortable": "true", "dataIndex": "alt"},
  {"header": "title", "sortable": "true", "dataIndex": "title"}
]
Главная страница: слайдер

Отличается этот способ от предыдущего тем, что у дополнительного поля слайдера теперь нет общей кнопки Загрузить файлы и Импорт из файловой системы, и файлы к каждому новому слайду загружаются вручную — в открывающемся окошке MIGX. В отличии от предыдущего метода, теперь можно дублировать одни и те же картинки. И такой «грубой» и безчувственной конфигурацией json — легче расширять простые массивы данных, например, можно создать для каждого слайда заголовок; этот код:

  {
    "field":"caption",
    "caption":"Заголовок"
  },

Добавь после поля title, Вкладки формы — и в результате, у каждого слайда появится на одно вводимое поле больше, сейчас добавится поле Заголовок. Чтобы поле отображалось в сетке, добавь следующую строчку на вкладку Разметка колонок, после строки с Изображением:

  {"header": "Заголовок", "sortable": "true", "dataIndex": "caption"},

Зафиксируй вывод этой новой строки на сайте, редактируй чанк: Элементы → Изображения → tpl.1.slider.slide (добавь строку 2):

<div class="one">
  <div class="caption">[[+caption]]</div>
[[$img?
  &imgThumb=`[[+image:phpthumbon=`&h=540&zc=0`]]`
  &alt=`[[+alt]]`
  &title=`[[+title]]`
]]
</div>

Еще раз, в чем отличие?

В прошлом методе - работала конфигурация, которая редактируется посложней, в визуальном редакторе ПриложенияMIGX, вкладка MIGX (она, кстати, продолжает работать на галерах и сейчас для галереи). Там, конечно, возможностей намного больше, но для начала разберись с json конфигурацией, так как для простых дополнительных полей этот способ позволит быстро их создавать и удобно расширять же. Кхмм? Читай дальше читатель!

Блок с преимуществами компании

Допустим, этот блок будет выглядеть так: картинка или иконка визуализирующая текущее преимущество, заголовок, текст описание и ссылка на страницу с подробным описанием. На моем тестовом сайте, я не рассматриваю css и js код, так как это тебя только запутает, при необходимости — сделаешь его сам. А для того, чтобы в голове у тебя что-то отложилось, я оперирую чистым html-кодом.

Вот пример, каким может быть этот блок, если оформить его подобающим образом:

Блок с преимуществами компании

Создай новый источник файлов для хранилища иконок. Ступай к МедиаИсточники файлов, скопируй источник Filesystem, отредактируй его:

Создай саму папку assets/files/icons/ — icons.

Элементы → Дополнительные поля → Главная

ПКМ, Создать новый TV здесь. Имя: tpl.1.benefits, Подпись: Преимущества компании, Порядок сортировки: 2.

Вкладка дополнительного поля Параметры ввода; Тип ввода: migx.

Вкладки формы:

[
{"caption":"Преимущество", "fields": [
  {
    "field":"icon",
    "caption":"Иконка",
    "inputTVtype":"image",
    "sourceFrom":"migx"
  },
  {
    "field":"link",
    "caption":"Ссылка"
  },
  {
    "field":"title",
    "caption":"Заголовок"
  },
  {
    "field":"about",
    "caption":"Описание",
    "inputTVtype":"richtext"
  }
]}
]

Разметка колонок:

[
  {"header": "Иконка", "width": "160", "sortable": "false", "dataIndex": "icon","renderer": "this.renderImage"},
  {"header": "Заголовок", "sortable": "true", "dataIndex": "title"},
  {"header": "Ссылка", "sortable": "true", "dataIndex": "link"}
]

Источники файлов: выбирай Хранилище иконок. Вкладка Доступно для шаблонов, ставь галочку напротив шаблона Главная страница.

Дополнительное поле преимущества компании

Заостри внимание! Этот источник файлов не привязывается к id ресурса, а почему? Все естественно: в папку /assets/files/icons/ будут загружаться иконки, и хорошо бы обладать к этому «Хранилищу иконок» постоянным доступом из любых других TV-полей. Ибо, я хочу: чтобы была возможность использовать эти иконки и дальше, не загружая их каждый раз заново на новых страницах, отличающихся по id (и еще, ибо, я хочу понтиак джитио 68 года). Тут нужно интуитивно понимать, как будет эксплуатироваться сайт, и если это понимание есть, у тебя не составит труда, назначить корректные источники файлов.

Редактирование дополнительного поля преимущества компании

Создай чанк для вывода на сайт: Чанки → Элементы → tpl.1.benefits.one

<div class="one">
  <div class="icon"><img src="[[+icon]]" alt="[[+title]]" /></div>
  <div class="title">[[+title]]</div>
  <div class="about">
    [[+about]]
    <a href="[[+link]]">Подробнее</a>
  </div>
</div>

В нужные места на главной странице (чанк tpl.1), добавь:

[[getImageList
  :toPlaceholder=`tpl.1.benefits`
  ?
  &tvname=`tpl.1.benefits`
  &limit=`0`
  &tpl=`tpl.1.benefits.one`
]]

<div class="benefits">
  [[+tpl.1.benefits]]
</div>
Все гениальное — просто
нужна только кучка практики

Как ты уже заметил, ничего сверхъестественного в создании уникальных полей для ввода данных, при помощи MIGX — нет. Используются всего две вкладки: Вкладки формы и Разметка колонок, а настройки дополнительного поля передаются массивом в формате json. Трам-пам. Вот-вот, доберешься до визуального конфигуратора и MIGXdb — аще слетишь с катушек, столько всего великого, там можно свершить со вселенской прытью: но, чтобы разобраться — приготовься основательно попотеть серой субстанцией.

Благие и лихие
утилиты для MIGX

У Василия, есть утилита, для проверки json. А, например, тот самый талантливый анонимус, подзапарился и набросал ценный генератор настроек MIGX, да так — что остается только скопировать итоговый код. Естественно, этот генератор не обладает всеми настройками, но основными возможностями наделяет, все остальное смотри в документации, к слову у этого же анонимуса есть норм-частичная документация MIGX на русском.

Следуя по мотивам
туториалов modx docs

Неизбежно надлежит рассмотреть еще одну дивную возможность MIGX, — ввод с разных форм и вывод на сайт с помощью плейсхолдеров. Суть такая — например, в контент сайта, менеджеру нужно без специальных знаний включать сложные блоки html: типа разбиения текста на необычайные колонки или какие-нибудь махинации с галереями изображениями. Конечно, не каждый менеджер, на такое способен, посему нужно максимально упростить миссию ввода этих самых данных. Да и намного удобнее, с такого рода задачами справляться плейсхолдерами, нежели копаться в исходниках визуального редактора или типа того. Ядреные помидоры, о чем это я?

Ядреные помидоры

Создай категорию Генератор контента, порядок сортировки 99.

В этой категории сотвори чанк tpl.generator.img, с кодом:

<div class="js-fancybox o-gallery">
    <img src="[[+img]]" title="[[+title]]" alt="[[+alt]]" />
</div>

Код, может быть естественно не обязательно таким, смысл в том — что ты его можешь настроить под определенные события js, с каким угодном html-ем, а обработанное содержимое будет выводится в контент сайта, ну просто супер легко.

Там же, сделай чанк tpl.generator.columns под колоночную верстку:

<div classs="columns">
    
    <div class="column_1">
        <div class="title">[[+title_1]]</div>
        <div class="text">[[+text_1]]</div>
    </div>
    
    <div class="column_2">
        <div class="title">[[+title_2]]</div>
        <div class="text">[[+text_2]]</div>
    </div>
    
</div>

Теперь нужно сделать дополнительное поле в категории Генератор контента, с названием tpl.generator, подпись Генератор сетки. Доступно для шаблонов: галочки на главную страницу, статические и т.д. везде где будем юзать. Источники файлов: Ресурс

Тип ввода: MIGX

Вкладки формы:

[

{
    "formname":"Картинка с увеличением"
    ,"formtabs": [{
        "caption":"Картинка с увеличением"
        ,"fields": [{
            "field":"placeholder"
            ,"inputTVtype":"hidden"
            ,"default":"grid"
        },
        {
            "field":"MIGX_chunk"
            ,"inputTVtype":"hidden"
            ,"default":"tpl.generator.img"
        },
        {
            "field":"alt"
            ,"caption":"alt"
        },
        {
            "field":"title"
            ,"caption":"title"
        },
        {
            "field":"img"
            ,"caption":"Изображение"
            ,"inputTVtype":"image"
            ,"sourceFrom":"migx"
        }]
    }]
},

{
    "formname":"Две колонки"
    ,"formtabs": [{
        "caption":"Две колонки"
        ,"fields": [{
            "field":"placeholder"
            ,"inputTVtype":"hidden"
            ,"default":"grid"
        },
        {
            "field":"MIGX_chunk"
            ,"inputTVtype":"hidden"
            ,"default":"tpl.generator.columns"
        },
        {
            "field":"title_1"
            ,"caption":"Заголовок 1"
        },
        {
            "field":"text_1"
            ,"caption":"Колонка 1"
            ,"inputTVtype":"richtext"
            ,"sourceFrom":"migx"
        },
        {
            "field":"title_2"
            ,"caption":"Заголовок 2"
        },
        {
            "field":"text_2"
            ,"caption":"Колонка 2"
            ,"inputTVtype":"richtext"
            ,"sourceFrom":"migx"
        }]
    }]
},

{
    "formname":"Тестовая сетка"
    ,"formtabs": [{
        "caption":"Тестовая сетка"
        ,"fields": [{
            "field":"placeholder"
            ,"inputTVtype":"hidden"
            ,"default":"grid"
        },
        {
            "field":"MIGX_chunk"
            ,"inputTVtype":"hidden"
            ,"default":"tpl.generator.test"
        },
        {
            "field":"test"
            ,"caption":"Тестовое поле"
        }]
    }]
}

]

Разметка колонок:

[
{"header": "Плейсхолдер", "sortable": "true", "dataIndex": "placeholder", "renderer": "this.renderPlaceholder"}
]
Что тут интересного?
!!!

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

инструмент для создания кастомных блоков

Во вторых, для того чтобы включить их содержимое в контент страницы, используются плейсхолдера, например: [[+grid.1]]

используются плейсхолдера

Чтобы плейсхолдеры обрабатывались при генерации страницы, добавь следующий код в начало той страницы, где будет производиться эта обработка или в общий шаблон:

[[getImageList? &tpl=`@FIELD:MIGX_chunk`&tvname=`tpl.generator`&toSeparatePlaceholders=`grid`]]

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

{
  "field":"placeholder"
  ,"inputTVtype":"hidden"
  ,"default":"grid"
}

Это скрытое поле placeholder, оно обрабатывается в название плейсхолдера, тот, что ты видишь в названии сетки.

{
  "field":"MIGX_chunk"
  ,"inputTVtype":"hidden"
  ,"default":"tpl.generator.test"
}

Это поле MIGX_chunk используется для подстановки нужного чанка при генерации контента.

{
  "field":"test"
  ,"caption":"Тестовое поле"
}

Это тестовое поле test для ввода данных.

Ограничений при расширении подобных блоков — тупо нет. Любой функционал сайта в итоге будет запросто смоделирован... Тут нужен только полет воображения, да что там говорить MIGXdb, так вообще позволяет еще и любые модули клепать, с такой же простотой, но эта уже тема для другой статьи.

Загенери еще посадочную страницу
(landing page)

По подобию, делается генерация посадочных страниц. Создай шаблон Посадочная страница, с кодом:

<!doctype html>
<html lang="ru">
<head>
[[MetaX?tpl=`мета-теги`]]
[[#1.tv.настройки.prehead]]
</head>
<body>

[[getImageList? &tpl=`@FIELD:MIGX_chunk`&tvname=`landing.generator`]]

[[#1.tv.настройки.prebody]]
</body>
</html>

Создай новую страницу, название Landing, шаблон: Посадочная страница. Галочка: не показывать в меню.

Далее иди в Настройку формРедактирование страниц и создай новый набор правил на действие Обновить ресурс, шаблон: Посадочная страница.

новый набор правил

Там убери галочку: modx-resource-content.

Для того, чтобы поле «Содержимое» не показывалось при редактировании посадочной страницы. Так как лендинги — как правило имеют блочную структуру, то смысл в этом поле по умолчанию отпадает. И сейчас, ты создашь генератор лендинга с блочной структорой, а я посмеюсь над тем, как ты неумело это делаешь. Задача такая, предположим что сам лендинг уже есть, остается только сделать к нему управление через админ-панель. Разрабатывается это все так же с помощью MIGX, а соль в том, что эти блоки можно будет мягкими взмахами мышки менять местами, расширять функционал и главное: ни о чем не парится.

На моем лендинге есть такие блоки:

Сначала, дополнительное поле, через которое будет собираться landing page, создай его в категории Генератор контента.

Далее в параметрах ввода, создадим чистый шаблон под лендинг, Вкладки формы:

[

]

Разметка колонок:

[{
    "header": "Блок"
    ,"sortable": "true"
    ,"width":"400"
    ,"dataIndex": "MIGX_formname"
}]

Landing page - блок преимущества

На самом деле, этот блок ты уже делал для главной страницы, поэтому, в inputTV указываем tpl.1.benefits. Добавь следующий код, меж двух квадратных скобок, на вкладке Параметры ввода → Вкладки формы:

{
    "formname":"Преимущества"
    ,"formtabs": [{
        "caption":"Преимущества"
        ,"fields": [{
            "field":"MIGX_chunk"
            ,"inputTVtype":"hidden"
            ,"default":"landing.benefits"
        },
        {
            "field":"title"
            ,"caption":"Заголовок"
        },
        {
            "field":"benefits"
            ,"caption":"Преимущества"
            ,"inputTV":"tpl.1.benefits"
        }]
    }]
}
Landing page - блок преимущества

Создай чанк Генератор контента → landing.benefits, с содержимым:

<div class="land-block benefits">
<h2>[[+title]]</h2>
<hr/>

[[getImageList
  ?
  &value=`[[+benefits]]`
  &limit=`0`
  &tpl=`landing.benefits.one`
]]

</div>

Создай чанк Генератор контента → landing.benefits.one

<div class="one">
  <div class="icon"><img src="/assets/files/icons/[[+icon]]" alt="[[+title]]" /></div>
  <div class="title">[[+title]]</div>
  <div class="about">
    [[+about]]
    <a href="[[+link]]">Подробнее</a>
  </div>
</div>

Тут как раз используется та самая бесконечная вложенность, коей я восхищался ранее.

Landing page - Блок заголовок + текст

На вкладку параметры ввода → Вкладки формы, после блока с преимуществами копируй этот код:

{
    "formname":"Заголовок + текст"
    ,"formtabs": [{
        "caption":"Заголовок + текст"
        ,"fields": [{
            "field":"MIGX_chunk"
            ,"inputTVtype":"hidden"
            ,"default":"landing.title.vs.text"
        },
        {
            "field":"title"
            ,"caption":"Заголовок"
        },
        {
            "field":"text"
            ,"caption":"Текст"
            ,"inputTVtype":"richtext"
        }]
    }]
}

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

Landing page - Блок заголовок + текст

Создай чанк Генератор контента → landing.title.vs.text:

<div class="land-block text-block">
  <h2>[[+title]]</h2>
  <hr/>
  [[+text]]
</div>

Landing page - Блок текст + картинка
(причем картинка слева или справа)

Добавь блок на вкладки формы:

{
    "formname":"Картинка + текст"
    ,"formtabs": [{
        "caption":"Картинка + текст"
        ,"fields": [{
            "field":"MIGX_chunk"
            ,"inputTVtype":"hidden"
            ,"default":"landing.img.vs.text"
        },
        {
            "field":"title"
            ,"caption":"Заголовок"
        },
        {
            "field":"text"
            ,"caption":"Текст"
            ,"inputTVtype":"richtext"
        },
        {
            "field":"img"
            ,"caption":"Картинка"
            ,"inputTVtype":"image"
        },
        {
            "field":"pos"
            ,"caption":"Картинка справа?"
            ,"inputTVtype":"checkbox"
            ,"inputOptionValues":"Да==1"
        }]
    }]
}
Landing page - Блок текст + картинка

Создай чанк Генератор контента → landing.img.vs.text

<div class="land-block text-block">
  <div class="image [[+pos:is=`1`:then=`float-right`:else=`float-left`]]">
    <img src="[[+img]]" />
  </div>
  <div class="text  [[+pos:is=`1`:then=`float-left`:else=`float-right`]]">
    <h2>[[+title]]</h2>
    [[+text]]
  </div>
</div>

Обрати внимание: поле pos, позволяет в верстке определить с какой стороны будет выводится картинка, в чанке это дело просто генерим при помощи phx, если галочка активна, устанавливаем нужный класс.

Landing page - Блок слайдер

Добавь блок на вкладки формы:

{
    "formname":"Слайдер"
    ,"formtabs": [{
        "caption":"Слайдер"
        ,"fields": [{
            "field":"MIGX_chunk"
            ,"inputTVtype":"hidden"
            ,"default":"landing.slider"
        },
        {
            "field":"slider"
            ,"caption":"Слайдер"
            ,"inputTV":"tpl.1.slider"
        }]
    }]
}

"inputTV":"tpl.1.slider", значит что используем уже готовое поле с главной страницы.

Landing page - Блок слайдер

Создай чанк Генератор контента → landing.slider:

<div class="land-block slider">
[[getImageList
  ?
  &value=`[[+slider]]`
  &limit=`0`
  &tpl=`landing.slider.slide`
]]
</div>

Создай чанк Генератор контента → landing.slider.slide:

<div class="one">
  <div class="caption">[[+caption]]</div>
[[$img?
  &imgThumb=`[[phpthumbon?input=`/assets/files/slider/[[*id]]/[[+image]]`&options=`&h=540&zc=0`]]`
  &alt=`[[+alt]]`
  &title=`[[+title]]`
]]
</div>

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

Landing page - итог
?!



Содержание:


26 марта 2017, 11:45
modx revolution


Авторизация
Зайди на сайт с помощью соц. сети: