Вывод по тегу php:

Прерыватель вывода Ditto
23 October 2009

Буду по возможности краток :) В 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
6 February 2009

В 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. Конечно, иногда можно обойтись и вовсе одиночным запросом к БД, но это уже совсем другая история.

Inherit Parent Template
19 January 2009

В 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;

Думаю, идея понятна. Всё само и никаких неудобств!

Костыль для ajaxSearch
16 January 2009

В 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, и что там может быть еще. Увы, мне лень :( Отсюда и название — костыль.

Комментариев всего
8 January 2009

Комментарии в 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
31 December 2008

Тэги в MODx делаются не сложно, сложнее решить как их сделать удачнее :) Ковыряние интернета дало несколько решений, общая их идея такова: в документе есть поле в котором лежат тэги, выводит тэги в должной кондиции сниппет tagLinks, документы с тегами выбирает сниппет Ditto. Различия решений сводятся к различным мелочам, я не буду писать каким и почему какие-то мне не понравились — это долго. В этом посте я расскажу как делались тэги на этом сайте, может кому что понравится — забирайте.

Первый шаг абсолютно очевидный: создаем tv-параметр «tags» типа text, и цепляем его ко всем документам которые должны иметь тэги. Поле заполняется тегами через запятую, всё элементарно.

Дальше необходимо это как-то выводить. Выводить тэги с помощью tagLinks мне не хотелось, поэтому я чуть-чуть попрогал. Почему? Потому, что задача банальна. Я открыл парсер PHx ( /assets/plugins/phx/phx.parser.class.inc.php ), и за строчкой ### String modifiers (у меня это 240-ая строка) вписал свой обработчик tags:

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+], и строка с тегами сама разбилась и вывелась должным образом. В связи с особенностями верстки пришлось обернуть это еще в проверку на пустоту с выводм &nbsp;, если строка пустая. Мне кажется, что такой вариант куда более шустрый, простой и понятный, чем ставить лишний сниппет, и курить его параметры чтобы получать ровно то же самое. Впрочем, если вам нравится поставить сниппет, то результат отличаться не будет.

Примечание: на данном этапе тэги надо выводить по-другому, и что за документ такой /tags/ — тоже не понятно. Однако дальше всё пояснится.

Итак, тэги принимает сниппет Ditto и выводит нужные документы. Не будем погружаться в смысл сниппетов и их реализацию в MODx, делается всё аналогично любой выборке документов с той лишь разницей, что добавляется три параметра:

&tagData=`tags` &tagDelimiter=`,` &tagMode=`onlyTags`

Описание всего этого можно найти в документации, а смысл понятен и так: это параметр, содержащий тэги, разделитель тэгов и режим «ловли», в данном случае «любой из перечисленных». Есть еще параметр &tags, который мы не пишем потому, что он цепляется из URL. Я создал документ с отдельным шаблоном, в котором документы выбираются с учетом тегов, этот документ располагается по адресу /tag/.

Теперь про URL. По идее мы пишем что-то типа /tag/?tags=some-tag, но, согласитесь, это как-то несолидно. Я подумал, что /tag/some-tag   как-то поприятнее будет, но так не работает. Чтож, берем мануал по htaccess и раскуриваем. Не углубляясь в подробности я привожу свое решение. Перед обработкой фрэндли-юэрэлов вписываем следующее:

# Tags
RewriteCond %{REQUEST_URI} /tag*
RewriteRule ^tag/([^.]+) index.php?q=/tag/&tags=$1 [L,QSA]

Мол, если полезли за тегами, то преобразовываем приятное в нужное. Может где накосячил, но вроде работает :)

Примечание: это решение для включенных ЧПУ с разрешением на вложенность

И еще небольшой штришок. На странице с тэгами надо как-то выводить, что выбран именно такой вот тег, для чего я решил воспользоваться надстройкой get над PHx. Втыкаем в нужное место [+phx:get=`tags`+] и радуемся :)

Вот и всё. Какие-то детали, рассчитанные на знающего MODx разработчика, возможно остались за кадром, но всё самое интересное я осветил. Естественно, любой шаг можно доработать и довести еще до ума, что-то я наверняка в будущем сам доведу до ума, но набросок решения именно такой.

1