Pdotools modx revo: pdoTools / Компоненты / docs.modx.pro – pdoTools / Классы / pdoTools / docs.modx.pro

pdoTools в MODX — замена стандартных сниппетов

pdoTools в MODX — замена стандартных сниппетов

«pdoTools» — это пакет, содержащий инструменты для быстрой разработки сниппетов в MODX Revolution, а так же набор готовых сниппетов для повседневной работы. Именно о этих сниппетах и пойдёт речь в статье, а точнее о том, как я заменял ими стандартные «сниппеты» и для чего это делал.

Так чем же хорош компонент «pdoTools»? А тем, что позволяет создавать сниппеты без использования xPDO — MODX’овской надстройки над PHP расширением — PDO. Небольшое пояснение по этому поводу. Изначально все запросы к базе данных осуществлялись на SQL (структурированный язык запросов), и все бы хорошо, но вот синтаксис этих запросов к различным СУБД (MySQL, MSSQL, Oracle, FireBird…) зачастую отличается. Приходилось затачивать код под определенную СУБД и переписывать его при использовании с другой СУБД. Чтобы избежать данного неудобства для PHP было написано расширение PDO, которое позволило создать универсальный интерфейс для работы с базами данных. В MODX пошли дальше и для удобства разработки написали надстройку над PDO, которую назвали xPDO. Эта надстройка делает работу с БД из под MODX (а мы помним, что MODX, это не просто CMS, это CMF) ещё более удобной и безопасной, но за всё надо платить и в нашем случае это скорость работы. xPDO в качестве результата запроса возвращает объекты и при большом количестве возвращаемых объектов (тысячи) время генерации страницы значительно возрастает. Поэтому наш соотечественник Василий Наумкин решил разработать собственный инструмент для создания сниппетов, которые работают через PDO, а не через xPDO. Как вы поняли, инструмент этот и есть «pdoTools».

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

Замена getResources на pdoResources

Сниппеты для вывода ресурсов. Про эту замену я писал отдельную статью, она получилась не слишком содержательной, но кому-то может и пригодилась. Пример замены сниппета «getRecources» на «pdoRecources» с привязкой к «pdoPage» чуть ниже.

Замена getPage на pdoPage

Сниппеты для постраничного вывода ресурсов в MODX Revolution.

Как было:


[[!getPage@articlePaging?
    &elementClass=`modSnippet`
    &element=`getResources`
    &parents=`36,37,38,39,40`
    &depth=`1`
    &limit=`5`
    &includeTVs=`1`
    &tpl=`articleTpl`
]]

«articlePaging» — это набор параметров, где хранится содержимое чанков сниппета для оформления вывода. Для «pdoPage» я приведу их содержимое, так как оно несколько отличается от имеющихся в «getPage».

Как стало:


[[!pdoPage?
    &parents=`36,37,38,39,40`
    &depth=`1`
    &limit=`5`
    &includeTVs=`img-news,tags,HitsPage`
    &tpl=`articleTpl`
    &tplPageWrapper=`@INLINE [[+prev]][[+first]][[+pages]][[+last]][[+next]]`
    &tplPage=`@INLINE <a href="[[+href]]" title="[[+pageNo]]">[[+pageNo]]</a>`
    &tplPageActive=`@INLINE <span>[[+pageNo]]</span>`
    &tplPageFirst=`@INLINE <a href="[[+href]]">First</a>`
    &tplPageLast=`@INLINE <a href="[[+href]]">Last</a>`
    &tplPagePrev=`@INLINE <a href="[[+href]]">«</a>`
    &tplPageNext=`@INLINE <a href="[[+href]]">»</a>`
    &tplPageFirstEmpty=``
    &tplPageLastEmpty=``
    &tplPagePrevEmpty=``
    &tplPageNextEmpty=``
]]

Во-первых, пропали параметры «&elementClass» и «&element». Указывать их есть смысл только если в качестве источника постраничного вывода используется не «pdoRecources». Вместо «&includeTVs=`1`» пришлось указать имена всех tv параметров, участвующих в выводе. Остальные изменения связаны исключительно с чанками. В этом примере я специально отказался от использования набора параметров, чтобы вы увидели какие плейсхолдеры в каких чанках используются.

Замена getResourcesTag на pdoResources

В состав компонента getRecources входит так же сниппет getResourcesTag, предназначенный для вывода ресурсов, привязанных к определенному тегу. Этот сниппет так же можно заменить на pdoResources. Как было:

[[!getResourcesTag?
    &parents=`36,37,38,39,40`
    &depth=`1`
    &tpl=`articleTpl`
    &limit=`6`
    &includeTVs=`1`
]]
Как стало:
[[!pdoResources? &parents=`36,37,38,39,40` &depth=`1` &tpl=`articleTpl` &limit=`6` &includeTVs=`img-news,tags,HitsPage` &where=`{"tags:LIKE":"%[[!getUrlParam? &name=`tag`]]%"}` ]]

Сразу обращаем внимание на параметр — &where=`{«tags:LIKE»:»%[[!getUrlParam? &name=`tag`]]%»}` В нём мы указываем критерий выборки ресурсов. Если простыми словами, то выбираем все ресурсы, у которых в TV параметре «tags» встречается слово, передаваемое в GET параметре «tag». А его, в свою очередь, мы вытаскиваем из URL с помощью сниппета getUrlParam, который вы можете установить из репозитория

Кстати, возможно есть и какие-то другие способы, если знаете, то поделитесь.

Замена breadCrumbs на pdoCrumbs

Как было:


[[Breadcrumbs? &amphomeCrumbTitle=`Главная`]]

Как стало:


[[pdoCrumbs?
    &showHome=`1`
    &outputSeparator=` » `
    &tplHome=`@INLINE Главная`
]]
После замены пришлось немного подредактировать CSS оформление выводимых хлебных крошек, так как «pdoCrumbs» в отличии от «BreadCrumbs» на выходе дает не список li, а просто набор ссылок, разделенных сепаратором, указанным в сниппете.

Замена Wayfinder на pdoMenu

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

[[pdoMenu? &startId=`0` &level=`2`]]

Замена GoogleSitemap на pdoSitemap

Сниппеты для генерации карты сайта. Заменяется только название. У меня на сайте есть отдельная статья по созданию карты сайта в modx revolution при помощи сниппета «GoogleSitemap» — Карта сайта в MODX Revolution (GoogleSiteMap). Для «pdoSitemap» делается всё аналогичным образом.


[[!pdoSitemap]]

Остальные сниппеты

В состав «pdoTools» входит ещё 2 сниппета. Я не применял их пока на практике, поэтому ничего писать про них не буду. Это сниппеты «pdoUsers» для вывода списка польователей сайта и «pdoNeighbors» для вывода соседних ресурсов.

На этом всё. Возможно, к тому моменту когда вы будете читать эту статью, появятся ещё статьи, связанные с пакетом «pdoTools». Найти их можно перейдя по соответствующему тегу. Про неточности в статье пишите в комментариях — буду исправлять.

pdoCrumbs / Сниппеты / pdoTools / docs.modx.pro

Сниппет для построения навигации в стиле хлебных крошек.

Хорошо заменяет BreadCrumb, работает с документами из любых контекстов и позволяет указывать различные условия для выборки ресурсов.

Сниппет обладает очень высокой скоростью работы, за счет выборки всех нужных элементов из БД за один запрос.

Параметры

Принимает все параметры pdoTools и некоторые свои:

ПараметрПо умолчаниюОписание
&showLog0Показывать дополнительную информацию о работе сниппета. Только для авторизованных в контекте «mgr».
&from0Id ресурса, от которого строить хлебные крошки. Обычно это корень сайта, то есть «0».
&toId ресурса для которого строятся хлебные крошки. По умолчанию это id текущей страницы.
&excludeСписок id ресурсов, которые нужно исключить из выборки.
&toPlaceholderЕсли не пусто, сниппет сохранит все данные в плейсхолдер с этим именем, вместо вывода не экран.
&outputSeparator\nРазделитель между крошками
&tplИмя чанка для оформления ресурса. Если не указан, то содержимое полей ресурса будет распечатано на экран.
&tplCurrentЧанк оформления текущего документа в навигации.
&tplMaxЧанк, который добавляется в начало результатов, если их больше чем &limit.
&tplHomeЧанк оформления ссылки на главную страницу.
&tplWrapperЧанк-обёртка, для заворачивания всех результатов. Понимает один плейсхолдер: [[+output]]. Не работает вместе с параметром &toSeparatePlaceholders.
&wrapIfEmptyВключает вывод чанка-обертки &tplWrapper даже если результатов нет.
&showCurrent1Выводить текущий документ в навигации.
&showHome0Выводить ссылку на главную в начале навигации.
&showAtHome1Показывать хлебные крошки на главной странице сайта.
&hideSingle0Не выводить результат, если он один единственный.
&directionltrНаправление навигации: слева направо «ltr» или справа налево «rtl», например для Арабского языка.

Шаблоны

ШаблонПо умолчанию
&tpl@INLINE <li><a href="[[+link]]">[[+menutitle]]</a></li>
&tplCurrent@INLINE <li>[[+menutitle]]</li>
&tplMax@INLINE <li>&nbsp;...&nbsp;</li>
&tplHome
&tplWrapper@INLINE <ul>[[+output]]</ul>

Примеры

Генерация хлебных крошек для текущей страницы:

[[pdoCrumbs]]

Генерация в ограничением по количеству пунктов:

[[pdoCrumbs?
    &limit=`2`
]]

Сниппет хорошо работает при вызове из pdoResources. Например, вот такой чанк:

<h4>[[+pagetitle]]</h4>
<p>[[+introtext]]</p>
[[pdoCrumbs?
    &to=`[[+id]]`
    &showCurrent=`0`
]]

Генерация микроразметки Schema.org в JSON-LD формате

{'!pdoCrumbs' | snippet : [
    'showHome' => 1,
    'tplWrapper' => '@INLINE <script type="application/ld+json">
{
 "@context": "http://schema.org",
 "@type": "BreadcrumbList",
 "itemListElement":
[ {$output} ]
}
</script>'
    'tplHome' => '@INLINE {
   "@type": "ListItem",
   "position": {$idx},
   "item":
   {
    "@id": "{$link}",
    "name": "{$menutitle}"
    }
  },'
    'tplCurrent' => '@INLINE {
   "@type": "ListItem",
   "position": {$idx},
   "item":
   {
    "@id": "{$link}",
    "name": "{$menutitle}"
    }
  }'
    'tpl' => '@INLINE {
   "@type": "ListItem",
   "position": {$idx},
   "item":
   {
    "@id": "{$link}",
    "name": "{$menutitle}"
    }
  },'
]}

Демо

Рабочий пример генерации хлебных крошек в результатах поиска mSearch3.

pdoSitemap / Сниппеты / pdoTools / docs.modx.pro

Быстрый сниппет генерации карты сайта для поисковых систем (sitemap.xml). Понимает параметры сниппета GoogleSitemap (конвертирует в собственные) и легко его заменяет.

Главная особенность — очень высокая скорость, по сравнению с аналогом. На сайте bezumkin.ru при 1700 страниц скорость генерации возросла в 12 раз, с 8.4 сек. до 0.7.

По умолчанию отключена проверка прав на доступ к документам. Это легко исправить включением параметра &checkPermissions (Внимание: замедляет работу!):

[[!pdoSitemap?
    &checkPermissions=`list`
]]

По возможности ресурсы лучше исключать из карты параметрами сниппета.

Параметры

pdoSitemap принимает все параметры pdoTools и некоторые свои:

ПараметрПо умолчаниюОписание
&sitemapSchemahttp://www.sitemaps.org/schemas/sitemap/0.9Схема карты сайта.
&forceXML1Принудительно выводить страницу как XML.
&priorityTVДополнительное поле, в котором указывается приоритет документа. Нужно добавить его в параметр &includeTVs

Шаблоны

&tpl

@INLINE <url>\n\t
<loc>[[+url]]</loc>\n\t
<lastmod>[[+date]]</lastmod>\n\t
<changefreq>[[+update]]</changefreq>\n\t
<priority>[[+priority]]</priority>\n
</url>

&tplWrapper

@INLINE <?xml version=\"1.0\" encoding=\"[[++modx_charset]]\"?>\n<urlset xmlns=\"[[+schema]]\">\n[[+output]]\n</urlset>

Приоритет и частота обновления для поисковиков устанавливаются в зависимости от последней даты изменения документа:

Времени с последнего обновления документаПриоритетЧастота обновления
Менее суток назад1.0daily
Более суток и менее недели назад0.75weekly
Более недели и менее месяца назад0.5weekly
Более месяца назад0.25monthly

Инструкция по созданию файла sitemap.xml

  1. Создайте новый документ в корне сайта. На вкладке Документ выберите пустой шаблон, укажите Заголовок документа (не важно какой), и псевдоним sitemap. Проверьте, чтобы стояли галочки «Опубликован» и «Не показывать в меню».
  2. Перейдите на вкладку Настройки и укажите «Тип содержимого» — «XML».
  3. Уберите галочку с «Использовать HTML-редактор» и сохраните документ.
  4. В содержимом ресурса вызовите только сниппет pdoSitemap (см. Примеры ниже).

Примеры

Обычный вывод карты сайта для текущего контекста. В большинстве случаев этого может быть достаточно:

[[pdoSitemap]]

Генерируем карту сайта только из определённый контейнеров:

[[pdoSitemap?
    &parents=`10`
]]

Исключаем ресурсы с id = 15 и 25, вместе с их потомками:

[[pdoSitemap?
    &parents=`10, -15,-25`
]]

А теперь исключаем id = 15 с потомками, а 25 — без:

[[pdoSitemap?
    &resources=`-25`
    &parents=`-15,10`
]]

Добавляем еще один контекст:

[[pdoSitemap?
    &resources=`-25`
    &parents=`-15,10`
    &context=`web,catalog`
]]

А вот так можно посмотреть лог выборки карты:

[[pdoSitemap?
    &resources=`-25`
    &parents=`-15,10`
    &context=`web,catalog`
    &showLog=`1`
    &forceXML=`0`
]]

Как вывести записи modx через pdoTools

Давайте разберемся с вами как выводить записи на modx с помощью pdotools, а именно через pdoPage. По порядку создадим шаблон, выведем туда дополнительные поля и отобразим это все на странице.

Шаг 1: создаем чанк-шаблон

Добавляем новый чанк. Пишем туда необходимые данные, например, url-записи, title-записи modx, дополнительные поля, дата публикации и т.п. В следующем коде:
1) [[+uri]] — урл страницы документа
2) pagetitle — заголовок страницы
3) дальше идет tv.screen:ifnotempty — это вызов дополнительного поля, если он не пустой
4) tv.status, tv.start, tv.minimum — еще дополнительные поля
5) такой вид tv.start:strtotime:date=%d.%m.%Y позволяет вывести данные в нужном формате (например, 25.04.2018)
6) publishedon:date=%d.%m.%Y — дата публикации

<div><a href=»[[+uri]]»>[[+pagetitle]]</a><br> [[+tv.screen:ifnotempty=`<a href=»[[+uri]]»><img src=»[[+tv.screen]]»></a><br>`]]</div> Статус: <strong>[[+tv.status]]</strong><br> Старт: [[+tv.start:strtotime:date=`%d.%m.%Y`]]<br> Дата публикации: [[+publishedon:date=`%d.%m.%Y`]] <br> Минимум: [[+tv.minimum]]<br> <a href=»[[+uri]]»>Подробнее…</a> <hr>

<div><a href=»[[+uri]]»>[[+pagetitle]]</a><br>

[[+tv.screen:ifnotempty=`<a href=»[[+uri]]»><img src=»[[+tv.screen]]»></a><br>`]]</div>

 

Статус: <strong>[[+tv.status]]</strong><br>

Старт: [[+tv.start:strtotime:date=`%d.%m.%Y`]]<br>

Дата публикации: [[+publishedon:date=`%d.%m.%Y`]] <br>

Минимум: [[+tv.minimum]]<br>

<a href=»[[+uri]]»>Подробнее…</a>

<hr>

Создание чанка для вывода шаблона pdopage modx

Шаг 2: выводим pdoPage на странице

На странице, где вы хотите выводить записи вызываем сниппет pdoPage. Я делаю его некешируемым, мой код выглядит вот так:

[[!pdoPage? &parents=`76` &includeTVs=`status, start, minimum,screen,tarrifs` &tpl=`shabindex` &limit=7 &tplPage=`@INLINE <span><a href=»[[+href]]»>[[+pageNo]]</a></span>` &tplPageWrapper=`@INLINE <div>[[+first]] [[+prev]] [[+pages]] [[+next]] [[+last]]</div> ` &tplPageActive=`@INLINE <span> <a href=»[[+href]]»>[[+pageNo]]</a> </span>` &tplPagePrev=`@INLINE <span><a href=»[[+href]]»>«</a></span>` &tplPageNext=`@INLINE <span><a href=»[[+href]]»>»</a></span>` &tplPageSkip=`@INLINE <span>…</span>` &tplPagePrevEmpty=`@INLINE <span>«</span>` &tplPageNextEmpty=`@INLINE <span><span>»</span></span>` &tplPageFirst=`@INLINE <span><a href=»/»>Первая</a></span>` &tplPageLast=`@INLINE <span><a href=»[[+href]]»>Последняя</a></span>` &tplPageFirstEmpty=`@INLINE <span><span>Первая</span></span>` &tplPageLastEmpty=`@INLINE <span><span>Последняя</span></span>` ]] [[!+page.nav]]

[[!pdoPage? &parents=`76` &includeTVs=`status, start, minimum,screen,tarrifs` &tpl=`shabindex` &limit=7 &tplPage=`@INLINE <span><a href=»[[+href]]»>[[+pageNo]]</a></span>`

&tplPageWrapper=`@INLINE

<div>[[+first]] [[+prev]] [[+pages]] [[+next]] [[+last]]</div>

`

&tplPageActive=`@INLINE <span> <a href=»[[+href]]»>[[+pageNo]]</a> </span>`

&tplPagePrev=`@INLINE <span><a href=»[[+href]]»>«</a></span>`

&tplPageNext=`@INLINE <span><a href=»[[+href]]»>»</a></span>`

&tplPageSkip=`@INLINE <span>…</span>`

&tplPagePrevEmpty=`@INLINE <span>«</span>`

&tplPageNextEmpty=`@INLINE <span><span>»</span></span>`

&tplPageFirst=`@INLINE <span><a href=»/»>Первая</a></span>`

&tplPageLast=`@INLINE <span><a href=»[[+href]]»>Последняя</a></span>`

&tplPageFirstEmpty=`@INLINE <span><span>Первая</span></span>`

&tplPageLastEmpty=`@INLINE <span><span>Последняя</span></span>` ]]

[[!+page.nav]]

Такой длинный код, так как тут сразу с шаблонами вывода навигации.

Выводим страницы на modx через pdopage

Давайте по порядку:
1) !pdoPage? — вызов некешируемого сниппета
2) &parents=76 — родительский элемент, с которого выводим документы
3) &includeTVs=status, start, minimum,screen,tarrifs — разрешаем вывод дополнительных полей
4) &tpl=shabindex наш шаблон вывода документов modx
5) &limit=7 — количество элементов на странице. Если больше и есть page.nav — будет навигация
6) page.nav — вывод навигации.

Нажимайте сохранить и если все нормально — получиться примерно вот так:

Пример шаблона вывода записей modx pdopage

Все тестировалось на modx версии 2.5.7-pl, версия pdoTools 2.11.0-pl.

Необходимые дополнения для MODX Revo

Краткий обзор необходимых дополнений для разработки сайтов на MODX Revo: наборы сниппетов, редакторы, оптимизаторы и другие незаменимые компоненты.

Необходимые дополнения для MODX Revo - gozman.space

Каким бы не был функционал вашего сайта вам понадобятся определённые дополнения для экономии времени и повышения удобства. Так как вы уже решили использовать CMS/CMF для своего проекта, то вы явно не горите желанием писать всё с нуля и строить велосипеды из костылей в одиночку! MODX Revo крайне гибкая CMS, которая позволяет построить максимально оптимизированные крупные веб-проекты любой сложности, но всё это может стать достаточно сложной задачей без определённых дополнений и компонентов.

К счастью для нас MODX имеет обширное комьюнити, которое изобретает и поддерживает множество полезных и незаменимых дополнений. Моя цель в данной статье — составить список самых необходимых дополнений для MODX Revo, которые несомненно смогут сэкономить вам время при разработке.

Дополнения и компоненты

Перед тем как идти далее, давайте разберёмся — чем отличаются дополнения от компонентов в MODX? 

Основное отличие их в том, что дополнения для MODX не могут модифицировать ядро MODX, в отличие от компонентов (Core Extensions). Компоненты и дополнения (обобщённо модули) устанавливаются абсолютно одинаково: Меню → Приложения → Установщик → Загрузить дополнения. После загрузки дополнений необходимо вернуться в установщик и установить загруженные дополнения.

Необходимые дополнения и компоненты для MODX

Ace

Ace — лучший редактор кода для MODX. Очень гибкий, поддерживает множество тем оформления и имеет Emmet прямо из коробки! Работает без тормозов даже с огромными файлами стилей популярных CSS фреймворков (Bootstrap, Foundation итд).

CKEditor

CKEditor — один из самых популярных WYSIWYG-редакторов текста для MODX, в последнее время стал быстро набирать популярность ввиду того, что интерфейс его конкурента TinyMCE не обновляется уже годами и не вписывается в админку. Имеет множество дополнений и тем, что поддерживает гибкую настройку типографии (стилей) и многое другое.

Translit

Компонент Translit предназначен для (не поверите) транслитерации алиасов ресурсов. Необходимый компонент для работы в MODX «Дружественных URL».

pdoTools

Небольшой, но очень полезный набор сниппетов, без которого мне уже сложно представить создание сайтов на MODX. pdoTools работает исключительно на PDO и делает это очень быстро! В состав pdoTools входят:

  • pdoRecources — для вывода списка ресурсов.
  • pdoMenu — для организации удобной генерации списков меню.
  • pdoPage — для создания постраничной пагинации результатов выборки других сниппетов (например pdoRecources).
  • pdoCrumb — для создания «bread crumbs»

И другие немаловажные компоненты, которые крайне необходимы при разработке сайтов. За pdoTools (и многие другие дополнения) стоит сказать огромное спасибо Василию Наумкину — человеку сделавшему очень многое для MODX!

Console

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

Formit

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

AjaxForm

Дополнение, позволяющее обрабатывать Formit на AJAX — чтобы отправлять, проверять и сохранять без перезагрузки страницы (как с PHP).

SEO Pro

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

MinifyX

Компонент, который склеивает все ваши скрипты и стили в два файла, и минифицирует их автоматически. Позволяет серьёзно увеличить скорость загрузки страниц и уменьшить количество запросов.

UpgradeModx и Updater

У MODX всегда была одна проблема — он не мог обновляться по кнопке из админки, как могли его конкуренты. Нужно было скачать обновление вручную, правильно его залить в файловую систему и обновить. 

Первое дополнение будет крайне полезно, поскольку позволяет решить проблему обновления MODX и добавляет панель с кнопкой обновления MODX в случае наличия новой версии. 

Второе же дополнение позволяет также проверять обновления всех дополнений установленых на сайте и обновлять их, чтобы не залезать каждый раз в «Приложения → Установки». Не так уж необходимо как первое, но всё же тоже немного облегчает взаимодействие с MODX.

Кстати, раз мы вскользь задели панели админки — советую сразу отключить панель канала безопастности и новостей MODX. В них редко бывает что-то важное, а на скорость загрузки админки они влияют не мало. Можно также отключить в настройках следующие ключи feed_modx_security_enabled и feed_modx_news_enabled. За всеми актуальными новостями из мира MODX советую следить из официальных социальных сетей или фангрупп/сайтов. 

Saturday, 26 November 2016

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *