Чтобы pdoMenu выводил документы в заданном порядке

Для того, чтобы pdoMenu выводил ресурсы в том порядке, в котором они перечислены в параметре resources, необходимо добавить пустой параметр sortby
{$_modx->runSnippet('pdoMenu', [
'parents' => '0',
'resources' => '7,9,10,11,8,12,13',
'sortby' => '',
])}

Вывод даты на MODx через Fenom

У документа Modx есть несколько дат, которые можно использовать для отображения на сайте:
  • Дата создания — поле createdon
  • Дата публикации — поле publishedon
  • Дата изменения — поле editedon
MODx хранит их в формате Timestamp, поэтому, чтобы вывести их в человеческом виде, нужно использовать модификатор Fenom
Например так:
{$publishedon | date : "d.m.Y"}

Стоит различать место вывода. Тут возможно несколько ситуаций.
1. Вывод непосредственно в шаблоне. Тогда поле будет доступна у объекта $_modx->resource
И вывод будет выглядеть так
{$_modx->resource.publishedon | date : "d.m.Y"}
2. В чанке сниппета, например, в чанке pdoResource или msProduct. Тогда выводим дату непосредственно из переменной
{$publishedon | date : "d/m/Y"}

И, чтобы настроить нужный формат вывода даты, используем дополнительный параметр, где указываем что и как выводить.
"d/m/Y" -> 12/02/2017
"d.m.Y" -> 12.02.2017
"d.m.Y H:i:s" -> 12.02.2017 17:30:12

Все возможные подстановочные символы можно найти на официальной документации PHP php.net/manual/en/function.date.php

Чтобы вывести текущее дату:
{'' | date : "d.m.Y"}

Так можно вывести через fenom текущий год:
{'' | date : "Y"}

Например, для вывода текущего года в копирайте:
© {'' | date : "Y"} - название компании

Превышение нагрузки на хостинге Timeweb

Итак, обратился клиент с проблемой — в контрольной панели хостинга Timeweb имеется открытый тикет с темой 'TIMEWEB> Критическая нагрузка на сервер.'. В тикете сообщается, что ваш аккаунт превышает допустимую нагрузку и перенесен на специальный сервер TMPB3. Далее приложены текстовые инструкции общего характера и никакой конкретики.
Читать дальше

Импорт из CSV

По многочисленным просьбам общественности написал скрипт импорта в miniShop2 из CSV. У нас уже проскакивало несколько вариантов, но все они как то не очень.

Особенности:


  1. Это не сниппет, это самодостаточный скрипт.
  2. Использует процессоры для импорта ресурсов и изображений.
  3. Умеет обновлять ресурсы по указанному ключевому полю.
  4. Поддерживает ТВ параметры и поля товаров, включая JSON типа size и color.
  5. Есть режим отладки, с подробным логом и обработкой только первой строки файла.
  6. Выводит сообщения через системный лог.

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

Скрипт входит в состав miniShop2 с версии 2.1.4 и лежит в /core/components/minishop2/import/csv.php. Запускать его можно как из браузера (если у вас открыта директория core), так и из консоли.

Если захотите его перенести в другое, более подходящее место, то нужно отредактировать строку


Читать дальше

Прикрепляем в письмо менеджеру Excel файл с данными заказа

Обратился ко мне заказчик по доработкам minishop2, чтобы в письма менеджеру добавлять файд Excel с данными заказа. Изначально подумал про счет какой-то, или накладную. Оказалось обычная таблица с четырьмя колонками: код товара, название, цена и кол-во. Сроки как всегда поджимают — сделать надо было еще вчера.
Собственно вот так выглядил шаблон, который нужно было заполнить:

Читать дальше

YML файл для маркета при использовании minishop2

Создаем документ и скрываем из меню.
У документа:
— указываем Тип содержимого — «XML»;
— замораживаем адрес, какой нам необходим.
— шаблон выбираем «Пустой»
— в содержимое добавляем:
Читать дальше

Раздел статьи для сайта через pdoPage

Решил и такой пример оставить, так как часто применяю, когда надо раздел статьи сделать или новости. Храню данные в Collection, а вывожу черех pdoPage. Также здесь в вызове присутствует указание на кастомные параметры "@styled" сниппета. В них переопределена разметка пагинации и соответственно стили. Удобно использовать, когда пагинация на всем сайте повторяется.
Читать дальше

Стрелочки рядом с переключателяи пагинации в slick slider


В slick slider эти навинационные кнопки прописаны через абсолютное позиционирование. По структуре находятся в разных блоках с навигацией. Подгонять расположение кнопок помогало только при фиксированном кол-ве элементов в слайдере. Было решено что-то придумать, не особо костыльное, но чтобы четко работало.
Идея в следующем — на JavaScript'е обернуть элементы пагинации в дополнительный контейнер, в него же поместить элементы отвечающие за прокрутку влево и вправо.

Написал вот такую функцию:
setArrows = function(selector) {
    $(selector+" .slick-dots").wrap("<div class='slick-dots__wrapper'></div>");
    $(selector+" .slick-dots__wrapper").prepend('<span class="slick-dots-prev"></span>');
    $(selector+" .slick-dots__wrapper").append('<span class="slick-dots-next"></span>');
    $(selector+' .slick-dots-prev').on('click', function(){
        $(selector).slick('slickPrev');
    });
    $(selector+' .slick-dots-next').on('click', function(){
        $(selector).slick('slickNext');
    });
};

Добавляем ее к вызову инициализации слайдера
var main_product_slider = false;
initMainProductSlider = function() {
    selector = '.product-slider';
    if ($(window).width()<1050) {
        if (!main_product_slider) {
            $(selector).slick({
                'autoplay': false,
                'arrows': false,
                'dots': true,
                'slidesToShow': 3,
                'slidesToScroll': 1,
                'initialSlide': 1,
                'infinite': false,
                'adaptiveHeight': true,
                'responsive': [
                    {
                        breakpoint: 750,
                        settings: {
                            vertical: false,
                            dots: false,
                            slidesToShow:2
                        }
                    },
                    {
                        breakpoint: 450,
                        settings: {
                            vertical: false,
                            dots: true,
                            slidesToShow: 1
                        }
                    }
                ]
            });

            setArrows(selector);
            $(selector).on('breakpoint', function(event, slick, direction){
                setArrows(selector);
            });

            main_product_slider = true;
        }
    } else {
        if (main_product_slider) {
            $(selector).slick('unslick');
            main_product_slider = false;
        }
    }
};

Для правильного отображения стрелочек добавляем стили. Кусок из scss файла
.slick-dots{
      &__wrapper{
        position: absolute;
        bottom: -30px;
        display: block;
        width: 100%;
        padding: 0;
        margin: 0;
        list-style: none;
        text-align: center;
        .slick-dots{
          position: relative;
          bottom: auto;
          display: inline-block;
          width: auto;
          line-height: 10px;
          vertical-align: middle;
        }
        .slick-dots-prev{
          height: 10px;
          display: inline-block;
          vertical-align: middle;
          position: relative;
          cursor: pointer;
          margin-right: 4px;
          line-height: 10px;
          &:before{
            display: inline-block;
            content: "";
            @include sprite($arrow-slider);
            transform: rotate(180deg);
          }
        }
        .slick-dots-next{
          height: 10px;
          display: inline-block;
          vertical-align: middle;
          position: relative;
          cursor: pointer;
          margin-left: 4px;
          line-height: 10px;
          &:before{
            display: inline-block;
            content: "";
            @include sprite($arrow-slider);
          }
        }
}

Вызов slickslider для адаптивного сайта

Для слайдеров/каруселей нравится использовать slick slider. Самая большая его ценность в умении делать зависимые карусели. Эта особенность делает его очень гибким для внедрения в верстку.
Однако есть и несколько проблем в его использовании. Например, при адаптивной верстки у него проблемы с перезапуском. Например, в десктопном исполнении нам не нужен слайдер, а должны выводится только несколько изображений. При постепенном уменьшении размера браузера должен активироваться слайдер, и потом уменьшаться кол-во видимых слайдов. При увеличении экрана — процесс в обратном порядке. В десктопе слайдер должен отключиться.
Как я понял, это должно было работать так
$('.proekt__list').slick({
	slidesToShow: 1,
	slidesToScroll: 1,
	dots: true,
	arrows: true,
	centerPadding: '0px',
	centerMode: true,
	focusOnSelect: true,
	adaptiveHeight: true,
	responsive: [{
		breakpoint: 99999,
		settings: "unslick"
	}, {
		breakpoint: 768,
		settings: {
			slidesToShow: 1,
			arrows: true,
		}
	}, {
		breakpoint: 374,
		settings: {
			slidesToShow: 1,
			arrows: false,
		}
	}]
});
И оно работает при первом запуске, но стоит несколько раз изменить размер окна браузера — и все перестает нормально работать.

Пришел к такому виду вызова slick слайдера как оптимальному (ИМХО). Так как не работает нормально параметр unslick.
var main_product_slider = false;
initMainProductSlider = function() {
    selector = '.product-slider';
    if ($(window).width()<1050) {
        if (!main_product_slider) {
            $(selector).slick({
                'autoplay': false,
                'arrows': false,
                'dots': true,
                'slidesToShow': 3,
                'slidesToScroll': 1,
                'initialSlide': 1,
                'infinite': false,
                'adaptiveHeight': true,
                'responsive': [
                    {
                        breakpoint: 750,
                        settings: {
                            vertical: false,
                            dots: false,
                            slidesToShow:2
                        }
                    },
                    {
                        breakpoint: 450,
                        settings: {
                            vertical: false,
                            dots: true,
                            slidesToShow: 1
                        }
                    }
                ]
            });
            main_product_slider = true;
        }
    } else {
        if (main_product_slider) {
            $(selector).slick('unslick');
            main_product_slider = false;
        }
    }
};
Добавляю вызов функции при загрузке страницы
$(function() {
  initMainProductSlider();
});
Вешаю выполнение на resize страницы
var doresizepage;
$(window).resize(function(){
    clearTimeout(doresizepage);
    doit = setTimeout(resizepage, 100);
});
function resizepage(){
    initMainProductSlider('.product-slider');
}