SYAN

SYAN

С нами с 22 февраля 2017; Место в рейтинге пользователей: #325

Помогите добить VideoGallery

Videogallery сохраняет в Json
{"video":"https://rutube.ru/video/embed/xxxxxxx","videoId":"xxxxx",
"image":"/assets/videoGallery/01/xxxx.jpg",
"title":"Название","desc":"Описание","videoDuration":"PTxxMxxS"}
Редактировать его кривыми ручками чревато, поэтому для редактирования по полям отредактировал и заменил файл core/components/videogallery/tv/tpl/tv.videoGallery.input.tpl, но при нажатии на кнопку «сохранить данные» из json массива удаляются video, image и videourl.

core/components/videogallery/tv/tpl/tv.videoGallery.input.tpl
{$array = []}
{if !empty($tv->value)}
    {$array = $modx->fromJSON($tv->value)}
{/if}
{if empty($array)}
    {$array = ['video' => '','videoId' => '','image' => '','title' => '','desc' => '','videoDuration' => '']}
{/if}
{if is_array($array)}
    {$array['title'] = htmlspecialchars($array['title'])}
    {$array['desc'] = htmlspecialchars($array['desc'])}
{/if}
{$json = $modx->toJSON($array)}
<form id="tvForm_{$tv->id}" style="margin:0; padding:0;">
    <div class="videogallery-wrapper">
        <div class="videogallery-form">
            <input type="text" id="vgUrl_{$tv->id}" class="videogallery-url" placeholder="Ссылка на видео для обработки"
                   value="{if isset($array['video'])}{$array['video']}{/if}">
            <div>
                <small>Вставьте в это поле ссылку на видео, чтобы её обработать.</small>
            </div>
            <div>
                <small id="vgError_{$tv->id}" style="color:red"></small>
            </div>
            <div class="videogallery-video" id="vgVideo_{$tv->id}"></div>
            <div class="videogallery-image" id="vgImage_{$tv->id}"></div>
            <div class="clear"></div>
        </div>
Вчера в 01:36
modx.pro
73
0

Видеогалерея на MIGX. Может кому понадобится (решено)

*Помогите толком разобраться, как редактировать поля title, desc, image, duration, которые находятся внутри json массива у видео в виде отдельных полей, а не кривыми ручками

Видеогалерея на MIGX

  1. Устанавливаем VideoGallery
  2. Создаем tv с именем video тип ввода VideoGallery
  3. Создаем tv с именем videogallery. Тип ввода migx, конфигурация videogallery, выбираем шаблон для отображения
  4. Создаем сниппет VideoJsonToPlaceholders
    ! ВНИМАНИЕ в п.2 удалите пробел между & quot;

    <?php
    // Получаем параметры
    $json = $modx->getOption('json', $scriptProperties, '');
    $prefix = $modx->getOption('prefix', $scriptProperties, 'json.');
    
    // 1. Проверяем наличие данных
    if (empty($json)) {
        return 'Ошибка: JSON‑строка пуста';
    }
    
    // 2. Удаляем все & quot; из строки ! Внимание в следующей строке удалите пробел между & quot;
    $json = str_replace('& quot;', '', $json);
    
    // 3. Исправляем экранированные слеши
    $json = str_replace('\\/', '/', $json);
    
    // 4. Парсим JSON
    $data = json_decode($json, true);
    
    if (json_last_error() !== JSON_ERROR_NONE) {
        return 'Ошибка JSON: ' . json_last_error_msg() . ' (строка: ' . htmlspecialchars($json) . ')';
    }
    
    // 5. Проверяем, что результат — массив
    if (!is_array($data)) {
        return 'Ошибка: JSON не содержит массив';
    }
    
    // 6. Функция для извлечения корневого адреса (с защитой от повторного объявления)
    if (!function_exists('getRootUrl')) {
        function getRootUrl($url) {
            if (!filter_var($url, FILTER_VALIDATE_URL)) {
                return null;
            }
    
            $parsed = parse_url($url);
            if (!$parsed || !isset($parsed['scheme']) || !isset($parsed['host'])) {
                return null;
            }
    
            $root = $parsed['scheme'] . '://' . $parsed['host'];
    
            if (isset($parsed['port'])) {
                $isDefaultHttp = ($parsed['scheme'] === 'http' && $parsed['port'] === 80);
                $isDefaultHttps = ($parsed['scheme'] === 'https' && $parsed['port'] === 443);
                if (!$isDefaultHttp && !$isDefaultHttps) {
                    $root .= ':' . $parsed['port'];
                }
            }
    
            return $root;
        }
    }
    
    // 7. Устанавливаем плейсхолдеры с обработкой
    foreach ($data as $key => $value) {
        // Приводим строки к UTF‑8
        if (is_string($value)) {
            $value = mb_convert_encoding($value, 'UTF-8', 'UTF-8');
        }
    
        // Если это поле video и содержит URL — извлекаем корневой адрес
        if ($key === 'video' && !empty($value)) {
            $rootUrl = getRootUrl($value);
            if ($rootUrl) {
                $modx->setPlaceholder($prefix . 'video_root', $rootUrl);
            }
            // Сохраняем исходный URL
            $modx->setPlaceholder($prefix . 'video', $value);
        }
    
        // Преобразуем videoDuration из PT... в оптимальный формат
        if ($key === 'videoDuration') {
            if (empty($value) || !is_string($value)) {
                $value = '0 с';
            } else {
                preg_match('/PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?/', $value, $matches);
                $hours   = isset($matches[1]) ? (int)$matches[1] : 0;
                $minutes = isset($matches[2]) ? (int)$matches[2] : 0;
                $seconds = isset($matches[3]) ? (int)$matches[3] : 0;
    
                if ($hours > 0) {
                    $value = sprintf('%02d:%02d:%02d', $hours, $minutes, $seconds);
                } elseif ($minutes > 0) {
                    $value = sprintf('%02d:%02d', $minutes, $seconds);
                } else {
                    $value = $seconds . ' с';
                }
            }
        }
    
        // Подставляем значения по умолчанию для критических полей
        switch ($key) {
            case 'title':
                if (empty($value)) {
                    $value = 'Без названия';
                }
                break;
            case 'desc':
                if (empty($value)) {
                    $value = 'Описание отсутствует';
                }
                break;
        }
    
        // Устанавливаем плейсхолдер (если он ещё не установлен через парсинг)
        if (!in_array($key, ['video', 'video_root'])) {
            $modx->setPlaceholder($prefix . $key, $value);
        }
    }
    
    return '';
  5. Создаем новое MIGx поле videogallery
  6. Экспортируем туда конфиг
    {
      "formtabs": [
        {
          "caption": "Видеогалерея",
          "print_before_tabs": "0",
          "fields": [
            {
              "field": "video",
              "caption": "Видео",
              "inputTV": "video"
            }
          ],
          "pos": 1
        }
      ],
      "contextmenus": "",
      "actionbuttons": "",
      "columnbuttons": "",
      "filters": "",
      "extended": {
        "actionbuttonsperrow": 4,
        "gridload_mode": 1,
        "has_jointable": "yes"
      },
      "permissions": {},
      "fieldpermissions": "",
      "columns": [
        {
          "header": "Рендер",
          "dataIndex": "render",
          "width": 1,
          "sortable": "false",
          "show_in_grid": 0,
          "renderer": "this.renderChunk",
          "renderchunktpl": "[[!VideoJsonToPlaceholders? &json=`[[+video]]` &prefix=`video.`]]"
        },
        {
          "header": "Название",
          "dataIndex": "video_title",
          "sortable": "false",
          "show_in_grid": 1,
          "renderer": "this.renderChunk",
          "renderchunktpl": "[[+video.title]]"
        },
        {
          "header": "Видео",
          "dataIndex": "video_video",
          "sortable": "false",
          "show_in_grid": 1,
          "renderer": "this.renderChunk",
          "renderchunktpl": "<iframe src=\"[[+video.video]]\" width=\"160\" height=\"90\" frameborder=\"0\"></iframe>"
        },
        {
          "header": "Превью",
          "dataIndex": "video_image",
          "sortable": "false",
          "show_in_grid": 1,
          "renderer": "this.renderChunk",
          "renderchunktpl": "<img src=\"[[+video.image]]\" width=\"160\">"
        },
        {
          "header": "Время",
          "dataIndex": "video_duration",
          "width": 100,
          "sortable": "false",
          "show_in_grid": 1,
          "renderer": "this.renderChunk",
          "renderchunktpl": "[[+video.videoDuration]]"
        },
        {
          "header": "videoId",
          "dataIndex": "video_videoId",
          "sortable": "false",
          "show_in_grid": "0",
          "renderer": "this.renderChunk",
          "renderchunktpl": "[[+video.videoId]]"
        },
        {
          "header": "Описание",
          "dataIndex": "video_description",
          "sortable": "false",
          "show_in_grid": 1,
          "renderer": "this.renderChunk",
          "renderchunktpl": "[[+video.desc]]"
        }
      ],
      "category": ""
    }
09 февраля 2026, 01:00
modx.pro
2
2 381
0

MiniShop3 - Не выводятся товары дополнительных категорий

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

{'!msProducts' | snippet : [
            'parents' => $_modx->resource.id,
            ]}
MODX 3.1.2-pl
MiniShop3 — 1.0.0-alpha
PHP 8.1
16 апреля 2025, 11:55
modx.pro
380
0

msFilter2 использование слайдера для выбора диапазона дат публикации

Помогите разобраться в выводом msFilter2
необходимо вывести слайдер с датами по аналогии с ценами

'filters' => ' publishedon:number',
 'tplFilter.outer.resource|publishedon' => 'tpl.mFilter2.filter.slider',
'tplFilter.row.resource|publishedon' => '@FILE chunks/filter_slider_year.tpl'
чанк filter_slider_year.tpl
{var $key = $table ~ $delimeter ~ $filter}
<div class="col-md-6">
    <label for="mse2_{$key}_{$idx}" class="d-flex align-items-center">
        {$title}
        <input type="text" name="{$filter_key}" id="mse2_{$key}_{$idx}" value="{$value| date : 'Y'}"
               data-current-value="{$current_value| date : 'Y'}" class="form-control ml-1"/>
    </label>
</div>
Слайдер с годами выводится, но фильтрация не производится
пытался отдельно дописать фильтр year в сниппете mFilter2, чтоб использовать как прописать 'filters' => ' publishedon:year,, но соображалки не хватило (
или может не использовать | date: 'Y' а как-то решить через css?
22 января 2024, 13:25
modx.pro
641
0

Сниппет для вывода ресурсов кириллицей в алфавитном порядке с разбивкой на 3 колонки

может кому пригодится немного модифицированный pdoAtoZ

вызов сниппета

$_modx->runSnippet("@FILE snippets/pdoatoz.php", [
'select' => 'id,pagetitle,uri,parent',
'limit' => '0',
'depth' => $cat_depth,
'parents' => $parents,
'tpl' => '@INLINE <a href="{$uri}" class="abc-item-list">{$pagetitle}</a>',
'tplDelimiter' => '</div><div class="col-lg-4 col-md-12 col-sm-12 col-sx-12">',
'ClassRow' => 'row col-12 abc-list pb-5',
'ClassCol' => 'col-lg-4 col-md-12 col-sm-12 col-sx-12',
'ClassIndex' => 'abc-index text-center',
'indexDelimiter' => ' '
])

сам сниппет

<?php
/**
 * pdoAtoZ
 */
 
$parents = $modx->getOption('parents', $scriptProperties, 0);
$select = $modx->getOption('select', $scriptProperties, 0);
$includeTVs = $modx->getOption('includeTVs', $scriptProperties, '');
$processTVs = $modx->getOption('processTVs', $scriptProperties, '');
$limit = $modx->getOption('limit', $scriptProperties, '10');
$depth = $modx->getOption('depth', $scriptProperties, '10');
$sortby = $modx->getOption('sortby', $scriptProperties, 'pagetitle');
$itemTpl = $modx->getOption('tpl', $scriptProperties, false);
$ClassRow = $modx->getOption('ClassRow', $scriptProperties, 'row');
$ClassCol = $modx->getOption('ClassCol', $scriptProperties, 'col');
$ClassIndex = $modx->getOption('ClassIndex', $scriptProperties, 'alpha-index');
$tplDelimiter = $modx->getOption('tplDelimiter', $scriptProperties, '');
$indexDelimiter = $modx->getOption('indexDelimiter', $scriptProperties, ' | ');

if (!$itemTpl) return;

$pdo = $modx->getService('pdoFetch');

//Get collection of resources
$items = $pdo->getCollection('modResource', array(
    'published' => true,
    'deleted' => false
), array(
    'parents' => $parents,
    'select' => $select,
    'includeTVs' => $includeTVs,
    'processTVs' => $processTVs,
    'limit' => $limit,
    'sortby' => $sortby,
    'sortdir' => $sortdir,
    'depth' => $depth
));

$total=count($items);
$column2=intval($total/3);
$column3=intval($total/3*2);
$counter=0;

//create an array keyed with alphabet letters

$alphas = array();
foreach (range(chr(0xC0),chr(0xDF)) as $v)
  $alphas[$v] = iconv('CP1251','UTF-8',$v);

$alphaGroups = array();
foreach($alphas as $key=>$value){
    $alphaGroups[$value] = array();
}

// organize the resources by first letter
foreach ($items as $item) {
$firstLetter = mb_strtoupper(mb_substr($item['pagetitle'], 0, 1));
$item['url'] = $modx->makeUrl($item['id']);
// check to see if first letter is a letter, if not don't do anything with it
if (!preg_match('/[А-ЯЁ]/u', $firstLetter)) continue;

    array_push($alphaGroups[$firstLetter], $item);
}

$alphaIndex = array();
$output = '';

$id = $modx->getOption('id', $scriptProperties, $modx->resource->get('id'));
$url = $modx->makeUrl($id);

foreach ($alphaGroups as $alpha => $alphaGroup) {
    if (count($alphaGroup) == 0) {
        
    } else {

        array_push($alphaIndex, '<a href="' . $url . '#' . $alpha . '">' . $alpha . '</a>');
        $formattedGroup = '';
        // to separate collumns    
        if ($counter >= $column2 && $col2 != 1 && $col2 != 1) { $formattedGroup .= $tplDelimiter; $col2 = 1;} 
        if ($counter >= $column3 && $col2 == 1 && $col3 != 1) { $formattedGroup .= $tplDelimiter; $col3 = 1;}
        $formattedGroup .= '<h3 id="' . $alpha . '" class="">' . $alpha . '</h3>'; 
        foreach ($alphaGroup as $item) {
            $formattedGroup .= $pdo->getChunk($itemTpl, $item);
            // uncomment the following line to see the item printed out in json format
            //$formattedGroup .= json_encode($item, JSON_PRETTY_PRINT);
        }
        $counter = $counter + count($alphaGroup);
        $output .= $formattedGroup;
    }
}

//return $alphaIndex + this list of resources grouped by letter;
return '<div class="'. $ClassIndex .'">' . implode($indexDelimiter, $alphaIndex) . '</div>' . '<div class="' . $ClassRow . '"><div class="' . $ClassCol . '">'. $output .'</div></div>';
19 января 2024, 17:51
modx.pro
1
873
0

Быстро пересоздать превью продуктов с выбором дапазона через Console

Кого не устраивает скорость пересоздания превью для продуктов через утилиту минишопа и необходимо пересоздать превью не для всех продуктов предлагаю вариант кода
Рекомендую:
1. отключить «Показывать ошибки» в консоле перед исполнением кода
2. увеличить max_execution_time php сервера для большого объема изображений

Актуально для больших каталогов и фотогалерей
14 декабря 2023, 23:39
modx.pro
822
0

Порядок вывода в SimpleSearch

Подскажите как организовать вывод в SimpleSearch сначала материалов определенного шаблона.
Из документации уяснил, что есть параметр sortBy, но как ему присвоить приоритет на вывод сначала всех материалов с шаблоном '4'?

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

{'!SimpleSearch' | snippet : ['sortBy' => 'template']}
07 февраля 2023, 12:15
modx.pro
764
0

Как подружить FrontEditor с множественными чекбоксами и MIGX?

При редактировании TV полей с MIGX с использованием FrontEditor получаю такой вид:

[{"MIGX_id":"1","title":"Название сайта 1","url":"http://адрес сайта 1"},{"MIGX_id":"2","title":"Название сайта 2","url":"http://адрес сайта 2"}]
При редактировании TV полей с чекбоксами с использованием FrontEditor выводятся только активные чекбоксы:
hide-image||hide-album||show_img_titles
Возможно ли привести их в человеческий вид для редактирования? Или возможно существует альтернативный редактор из фронтэнда?
09 мая 2022, 02:49
modx.pro
649
0

Автоматический редирект с параметрического адреса сайта на дружественный урл

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

Помогите настроить автоматический редирект с параметрического адреса сайта на дружественный урл
то есть при переходе по ссылке адрес.сайта/index.php?id=1 в браузере должен открыться адрес адрес.сайта/index.html и так для всех ресурсов сайта
09 мая 2022, 00:25
modx.pro
893
0

TinyMce RTE выбор изображений только из галереи ресурса

Возникла задача использовать в контенте msProducts изображения из его же галереи
Как научить TinyMce RTE по умолчанию при вставке картинки открывать папку с изображениями из assets/images/gallery/id_ресурса/, а не из modx.default_media_source?

Пробовал сменить url через tinymcerte.min.js, но как-то никак \_o_/

увидел в готовых решениях https://modx.pro/solutions/18899, но выскакивает ошибка при инициализации плагина (плагин заработал после дегрейта редактора с 5 на 4. спасибо автору за труд)
01 ноября 2021, 23:58
modx.pro
2
1 300
0