Вывод по тегу modx:
Буду по возможности краток :) В CMS MODx есть модуль Ditto, который позволяет выводить списки. С помощью него выводятся новости, документы раздела, и прочие однотипные сущности. Пример:
<div>
<span>Иванов Иван</span>
<span>Петров Петр</span>
<span>Сидоров Сидр</span>
...
</div>
Всё здорово, однако что делать если надо прерывать вывод списка? Вот как-то так:
<div>
<span>Иванов Иван</span>
<span>Петров Петр</span>
</div>
<div>
<span>Сидоров Сидр</span>
...
</div>
Ага, одни тут же вспомнили про [+ditto_iteration+], а другие, покопавшись в MODx Wiki, нашли [+phx:evenodd+]. Всё верно: копирем тот сниппет, немного редактируем, ставим в конце чанка-шаблона [+ditto_iteration:evenodd+], и получаем требуемый результат.
А что делать если надо не через два? Ведь чет и нечет пролетают мимо. Правильно — можно взять всё тот же сниппет и переписать его "через каждые 3", ничего сложного.
И всё это здорово, но есть пара НО:
1) Каждый раз прерывать поток сознания на переписывание таких глупостей весьма глупо.
2) Если в проекте надо и через 2, и через 3, и через 7, да еще и прерыватели везде разные, то хочется это как-то автоматизировать.
Собственно, после пары подобных ситуаций я немного переписал тот самый phx:evenodd, в результате чего предлагаю вам ознакомиться с phx:evenoddx. Разница, если кто не понял, в последней букве.
Сам сниппет:
<?php
$x = explode('::', $options);
$num = is_numeric($x[0]) ? $x[0] : 3;
$html = isset($x[1]) ? $x[1] : '<div></div>';
if (!($output % $num) && ($output))
return $html;
else
return ' ';
?>
Ну, если кто не в курсе, то надо создать сниппет, назвать его phx:evenoddx, и записать в него вышеприведенный код. И, разумеется, иметь установленный phx. После этого в конце чанка, который будет использовать Ditto, вызываем наш сниппет вот так:
[+ditto_iteration:evenoddx+]
Если параметров нет (как в этом примере), то на каждый 3-ий вывод будет генерироваться '</div><div>'. Таким нехитрым образом решается последняя задача из вышеприведенных (она весьма часта). Но можно написать и так:
[+ditto_iteration:evenoddx=`5`+]
— '</div><div>' будет выводиться через каждые 5 элементов.
[+ditto_iteration:evenoddx=`17::</span><span class="pictures">`+]
— Через каждые 17 будет выводиться '</span><span class="pictures">'. Двойное двоеточие — это просто такой разделитель.
Таким образом задача прерываемого перечисления элементов с помощью Ditto решается однажды и навсегда, можно через сколько угодно элементов вставлять любой прерыватель. Пользуйтесь на здоровье, если кому понадобится. С удовольствием узнаю ваши варианты решения подобной проблемы.
В MODx, к сожалению, нет инструментов для массовой обработки документов. Например, можно легко перемещать документ с дочерней структурой, но нельзя переместить несколько разных документов одновременно. Или, скажем, требуется изменить шаблон всем документам раздела, или отключить кеширование целой ветви в структуре. Как быть? Сидеть и перетыкивать галочки полутысяче документов? Нет! Ведь все эти галочки — это просто поля в БД, всё можно автоматизировать. Даже с нуля скрипт для перебора пишется за десяток-другой-третий минут, т. е. это быстрее, проще, надежнее и интереснее, чем руками перебирать сотню-другую документов. А с этим постом всё сводится вообще к минутам.
Моё решение состоит из двух частей: автоподключатор и автоперебиратор :)
Заранее прошу прощения за оформление кода, как доделаю сайт, возьмусь и за это.
Автоподключатор — это код, который вытаскивает данные о БД из конфигурационного файла MODx и устанавливает соединение с БД где бы скрипт не лежал (в пределах хостинга, разумеется). Всё полностью автоматически.
Читаем конфигурацию в массив строк:
// качаем файл настроек
$lines = file($_SERVER[’DOCUMENT_ROOT’] . ’manager/includes/config.inc.php’,
FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if (!$lines)
die (’check path to config.inc.php’);
Красный фрагмент — это то, что не должно работать в PHP 4, фрагмент надо будет стереть. Возможно, что при этом придется обтримить строки или еще как-то доделать скрипт.
Перебираем и выполняем нужные строки, получаем инициализированные параметры:
// съедаем данные
foreach ($lines as $line_num => $line)
{ if (strpos($line, ’rror_reporting’))
break;
if (strpos($line, ’tab’))
eval ($line);
if (strpos($line, ’dbase’))
eval (str_replace(’`’, ’’, $line));
}
Подключаемся к БД:
// подключаемся
$link = mysql_connect($database_server, $database_user, $database_password);
if (!$link)
die(’Could not connect: ’ . mysql_error());
$db_selected = mysql_select_db($dbase, $link);
if (!$db_selected)
die (’Can’t use ’ . $dbase . ’ : ’ . mysql_error());
echo «Connection OK.»;
Вот и всё. Написано не ахти, но работает универсально, стабильно и полностью само. Копируем код в скрипт и кладем на хостинг с MODx, получаем автоконнект с БД.
Автоперебиратор — это код, который рекурсивно перелопачивает документы в MODx совершая с ними некоторые действия. На входе только идэшник документа-корня. Перебираются все дочерние документы, и дочерние этих дочерних и т.д. Соответствующая рекурсивная функция:
// функция перебора
function do_something ($id)
{ GLOBAL $link;
$req = «UPDATE modx_site_content SET cacheable=0 WHERE parent=» . $id;
$res = mysql_query($req, $link);
if ($res)
{ $req = «SELECT id FROM modx_site_content WHERE isfolder=1 AND parent=» . $id;
$res = mysql_query($req, $link);
while ($row = mysql_fetch_array($res))
do_something($row[0]);
}
else
die(mysql_error());
}
Этот пример отключает кеширование. С документом-корнем изменения не происходят, перебираются только дочерние. Само действие выделено зеленым, если надо делать что-то отличное от отключения кеширования, то изменяем именно зеленый фрагмент. Если углубляться в структуру не требуется, то можно отключить селект.
Начало работы — вызов обработки корня, закрытие соединения и сообщение об окончании:
//поехали
do_something(14);
mysql_close($link);
echo ’All done’;
Внимание! Рекомендую бэкапить БД перед обработкой, мне не удалось что-либо сломать, но я не исключаю такой возможности :) Также рекомендую не торопиться, а продумывать изменения в БД. И последний совет, рабочий скрипт лучше прерывать в самом начале каким-нибудь exit(); пока не используется, а то можно неудачно запустить его в неподходящий момент. Заодно будете перепроверять скрипт когда он вдруг будет не срабатывать :)
Естественно, можно использовать автоподключатор для каких-то других целей вплоть до создания бэкдоров, и автоперебиратор сам по себе также для чего угодно, например, его можно засунуть в сниппет. На базе этого примера вообще много чего интересного можно собрать ;)
P.S. Конечно, иногда можно обойтись и вовсе одиночным запросом к БД, но это уже совсем другая история.
В MODx есть маленький такой плагинчик, Inherit Parent Template называется. Он состоит из десятка строк и призван помогать документам наследовать шаблон родителя, т. к. сам MODx предлагает настроить только шаблон по умолчанию для всей системы.
К сожалению, бывает такое, что документ не должен наследовать шаблон родителя. Как тогда быть?
Вариантов решения проблемы я знаю три. Первый: забить на всё, пусть пользователь переключает шаблон каждый раз руками. Понятно, что он будет забывать, ошибаться, и это неправильно. Второй: хранить дочерние документы в другом разделе который будет иметь нужный шаблон. Тогда возникает ряд неудобств, но зато всё само. И третий вариант: чуть-чуть обработать напильником упомянутый выше плагин.
Открываем плагин и вписываем после конструкции присваивания нужного шаблона (там сложно ошибиться):
if ($parent[’template’] == 4) // news template
$content[’template’] = 3;
Нужные номера шаблонов можно посмотреть в соответствующем разделе, они там высвечиваются. Сохраняем изменения. Теперь если родитель имеет шаблон #4, то дочерний документ будет иметь по умолчанию шаблон #3.
И тут можно было закончить, но есть еще одна фишка. Ровно таким же способом можно задавать другие исключительные значения параметров документа по умолчанию. Например, по всему сайту стоит «умолчательное кеширование», однако в каком-то разделе вам кеширование не нужно. Дописываем туда же:
if ($parent[’template’] == 7) // gallery template
$content[’cacheable’] = 0;
Аналогично можно оттолкнуться и от параметров «этого» документа вместо родителя, используя массив $content:
if ($content[’isfolder’] == 1) //
$content[’searchable’] = 0;
Думаю, идея понятна. Всё само и никаких неудобств!
В MODx есть сниппет для поиска — ajaxSearch. Как и все остальные стандартные сниппеты, он очень прост в использовании. Об этом может расскажу когда буду делать поиск по этому сайту, а пока расказываю про изобретенный костыль. Сам не забуду что да как, и другим может пригодиться. Если кто доведет до ума — вэлкам.
Проблема следующая: в результатах поиска сниппет иногда выдает на краях цитат спецсимволы. Очевидно, он режет utf-8 символ, в результате чего вместо нормального символа получается дрянь. В ФФ и Опере это выглядит как вопрос в черном ромбике, а в ИЕ как квадратик. Вот — ?.
Найти функцию обрезания (извините) очень просто, она лежит в файле /assets/snippets/ajaxSearch/includes/ajaxSearch.inc.php и гордо зовется SmartSubstr. Не будем вдаваться в задумку авторов и историю названия.
Итак, я решил проверять первый символ и смещать обрезатель если он приходится на блок utf:
if (ord($text[$halfside-1]) == 208 || ord($text[$halfside-1]) == 209)
$halfside++;
Естественно, после этого надо проверить и последний символ тоже:
if (ord($text[$halfside+$length-1]) == 208 || ord($text[$halfside+$length-1]) == 209)
$length++;
Вставляем все это прямо перед return и наслаждаемся, у меня всё исправилось. Если у вас нет, то можете поиграть «плюсадинами». Или вывести коды, возможно у вас какой-нибудь 207. Я не стал вникать и перебирать варианты.
Для веточки else, когда строка обрезается не с двух сторон а только с конца, также вставляем код:
if (ord($text[$length-1]) == 208 || ord($text[$length-1]) == 209)
$length++;
Оговорка: конечно, по идее надо делать более правильно для чего неплохо бы воткнуть в спецификации utf-8, разобраться что за 208 и 209, и что там может быть еще. Увы, мне лень :( Отсюда и название — костыль.
Комментарии в MODx делаются с помощью сниппета Jot. Пишем в шаблон [!Jot!] — и вот вам полноценные комментарии. Подсыпав пару параметров можно включить каптчу, премодерацию, автоуведомление автора о новых комментариях и прочие вкусняшки. Кстати, очень интересна разница между русскоязычной и оригинальной документацией, в последней даже рецепты есть, а в первой и о параметрах не сказано. Такое, к сожалению, не редкость.
Так вот, вся сложность включения комментариев на этом сайте была в кастрации превращении навороченного ввода-вывода в то, чем он является сейчас. Пришлось перековырять стандартные шаблоны, потом довести до ума стандартные стили, а потом еще подкрутить всё чтобы валидация xHTML прошла. Вуаля — на сайте комментарии.
А потом потребовалось, по заявкам радиослушателей, добавить к дайджесту (ничего, что я так?) сообщение о количестве имеющихся комментариев. И это оказалось задачкой, пам-пам-пам, так как в сниппете нет такого параметра :) Типа, «а такое мы не проходили» :)
В API MODx я не нашел как можно обратиться к полям других сниппетов. У самого Jot как такового API вообще нет насколько я понимаю. Пораскинув мозгами придумал два варианта: либо дописывать функционал Jot-а до такой фичи, либо писать фичу отдельно. Решил писать отдельно чтобы ничего не уронить :)
Залезаем в БД и вникаем в структуру таблиц Jot-а, в одной из них как раз контент комментариев. Комментарии привязываются к документам через uparent. Сочиняем запрос, которым вытаскиваем количество комментариев. Входными опшнсами по-любому управляю я, поэтому ничего экранировать и проверять не стал.
@$result = mysql_query("SELECT COUNT(id) FROM modx_jot_content WHERE uparent = ". trim($options)) or $out = 'err';
Затем проверяем а не ошибка ли получилась.
if ($out != 'err')
Если нет, то разбираем ответ и выводим количество. Потом в одном месте засвербило и захотелось выводить со склонениями :) А потом еще засвербило и захотелось ничего не выводить если комментариев нет.
{ $out = mysql_fetch_array($result); // разбираем ответ
if ($out = $out[0]) // если там не ноль, то
{ $mod = $out % 10; // находим отстаток от деления на 10
$end = 'ев'; // для большинства окончание -ев
if ($mod == 1) $end = 'й'; // для 1, 31, 141 -й
if (($mod > 1) && ($mod < 5)) $end = 'я'; // см. выше
$out = '<div class="comments">' . $out . ' комментари' . $end . '</div>';
}
else // если комментариев нет
$out = '<!-- нет комментариев -->';
}
Ну а если запрос вернул ошибку, то выводим её, но чтобы никто не заметил :)
else
$out = '<!--' . mysql_error() . '-->';
В результате возвращаем что бог послал.
return $out;
Вот и всё. Засовываем всё это в сниппет, я свой назвал phx:comments. В шаблоне теперь записываем [+phx:comments=`[+id+]`+] (где [+id+] — это ИДэ документа) и радуемся, т.к. в нужном месте будет всплывать дивчик класса comments с нужной записью. Или не будет всплывать чтобы не мозолить глаза фразой «нет комментариев». Естественно, всё тоже самое можно распилить и собрать по-другому, но это уже вопрос вкусов.
Тэги в MODx делаются не сложно, сложнее решить как их сделать удачнее :) Ковыряние интернета дало несколько решений, общая их идея такова: в документе есть поле в котором лежат тэги, выводит тэги в должной кондиции сниппет tagLinks, документы с тегами выбирает сниппет Ditto. Различия решений сводятся к различным мелочам, я не буду писать каким и почему
Первый шаг абсолютно очевидный: создаем
Дальше необходимо это
case «tags»:
$tgs_arr = explode(’,’, $output);
for($tgs_i = 0; $tgs_i
$tgs_arr[$tgs_i] = ’’ . trim($tgs_arr[$tgs_i]) . ’’;
$output = implode(’, ’, $tgs_arr);
break;
После этого я просто написал в шаблоне [+tags:tags+], и строка с тегами сама разбилась и вывелась должным образом. В связи с особенностями верстки пришлось обернуть это еще в проверку на пустоту с выводм , если строка пустая. Мне кажется, что такой вариант куда более шустрый, простой и понятный, чем ставить лишний сниппет, и курить его параметры чтобы получать ровно то же самое. Впрочем, если вам нравится поставить сниппет, то результат отличаться не будет.
Примечание: на данном этапе тэги надо выводить
Итак, тэги принимает сниппет Ditto и выводит нужные документы. Не будем погружаться в смысл сниппетов и их реализацию в MODx, делается всё аналогично любой выборке документов с той лишь разницей, что добавляется три параметра:
&tagData=`tags` &tagDelimiter=`,` &tagMode=`onlyTags`
Описание всего этого можно найти в документации, а смысл понятен и так: это параметр, содержащий тэги, разделитель тэгов и режим «ловли», в данном случае «любой из перечисленных». Есть еще параметр &tags, который мы не пишем потому, что он цепляется из URL. Я создал документ с отдельным шаблоном, в котором документы выбираются с учетом тегов, этот документ располагается по адресу /tag/.
Теперь про URL. По идее мы пишем
# Tags
RewriteCond %{REQUEST_URI} /tag*
RewriteRule ^tag/([^.]+) index.php?q=/tag/&tags=$1 [L,QSA]
Мол, если полезли за тегами, то преобразовываем приятное в нужное. Может где накосячил, но вроде работает :)
Примечание: это решение для включенных ЧПУ с разрешением на вложенность
И еще небольшой штришок. На странице с тэгами надо
Вот и всё.
