понедельник, 28 июля 2008 г.

Легко и просто определить, авторизован менеджер или нет

Задача: Находясь на фронт-энде (страницах сайта), нам нужно определить, авторизован ли менеджер или нет и, например, вывести на страницу какой-либо элемент только для менеджера. Я использую это в собственном блоке редактирования сайта с фронт-энда.

Решение: Используем переменную сессии MODx mgrValidated. Она равна 1, если менеджер сайта авторизован.

Например:

<?php
return $_SESSION['mgrValidated']==1 ? $modx->getChunk('chunk-for-manager-only') : '';
?>

среда, 28 мая 2008 г.

замена папки "manager" для администраторской панели ISPmanager (VPS)

Проблема: вы покупаете VPS-сервер, скажем на расчудесной Агаве, и вам в качестве системы управления этим сервером предлагается (в моем случае выбора не было) ISPmanager. Так вот адрес административной панели ISPmanager, как правило, выглядит следующим образом: http://имя_домена/manager. Как же нам установить MODx? Поддержка Агавы, заявила, что изменить этот адрес нельзя, но можно поиграть с mod_rewrite... Итак. Допустим что у нас нет времени на игры с mod_rewrite и нет желания менять папку manager в самой установке MODx.

Решение: Меняем папку для ISPmanager. "manager" как адрес админ-панели ISPmanager для сервера всего лишь псевдоним (alias), физически этой папки не существует. Как известно, псевдонимы прописываются в файле конфигурации Apache. Открываем файл конфигурации (ISPmanager->Инструменты->Менеджер файлов... путь: /etc/httpd/conf/httpd.conf, конечно же, это мой частный случай), ищем строку "manager". Мне не удалось найти ее, и я было отчаялся, однако вспомним, что в файл конфигурации можно включать внешние блоки директивой Include. Ищем слово "include". На этот раз успешно: найдена интересная строка
Include /usr/local/ispmgr/etc/ispmgr.inc

Открываем файл ispmgr.inc и находим строку
Alias /manager /usr/local/ispmgr/bin/
Меняем /manager на, скажем, /ispmanager и перезапускаем сервер.

И вот! Админ-панель ISPmanager находится по адресу http://имя_домена/ispmanager, а мы спокойно можем приступать к установке MODx.

четверг, 20 марта 2008 г.

вопрос этикета

сегодня случайно наткнулся на любопытный ресурс infinitiv.org.ua. Это недоделанная копия моего текущего проекта. Домен infinitiv.org.ua зарегистрирован на Максима Рогальского, ранее принимавшего участие в жизни modx.ru.

Уважаемый Максим... какого черта происходит?

UPD:
а вот еще сайты...
http://intway.zp.ua/

вторник, 18 марта 2008 г.

"Гибридный" вывод новостей (дочерние документы+категории)

Мы привыкли делать вывод новостей, статей, каталога продукции и тд., находящихся в определенной папке. То есть, имеем мы категории новостей (например: Новости шоу-бизнеса, Интервью, Мнения ...), они же у нас родительские документы к самим этим новостям. Есть и другие способы вывода, может быть из других родительских документов, из множества родительских документов, но суть одна - мы всегда выводим статьи из каких-либо папок. А что если какие-то документы, находясь в одной папке, должны выводиться и в других категориях (папках)? Не дублировать же их всюду?

Недавно столкнувшись с такой вот задачей "кросспостинга", я придумал следующее решение. Сразу замечу, можно легко решить проблему "свалив" все документы в одну папку и настроить категории только с помощью TV. Ditto при этом настроить элементарно. Однако в мою задачу входит сохранение уже существующей структуры папок и документов в них.

Для начала нужно определиться - документ в БД должен существовать в единственном экземпляре, никаких дублей! Задать категории, в которых он должен отобразиться помимо своей родительской мы можем с помощью TV (например типа список checkbox). Единственной проблемой теперь встает настройка вывода Ditto, который должен вывести все документы из родительского документа а также проверить принадлежность к заданной категории документов из других папок. На ум сразу же пришло использование параметра &filter, который может "отсеять" документы не принадлежащие к заданной папке И (именно логическое AND) не содержащие в определенном TV "упоминаний" о текущей категории. Однако есть досадная деталь: множество фильтров Ditto (например &filter=`test1,34,2|test2,78,1` поддерживают лишь логическое ИЛИ). Единственным выходом оказалось изучение и написание собственного (custom) фильтра. Сделать это можно на основе примерного фильтра в файле example.extender.inc.php. И подключить его параметром &extenders. не буду вдаваться в подробности кода, там достаточно все элементрано:
<?php
// ---------------------------------------------------
// Group: Filters
// Define custom or basic filters within the extender to expand Ditto's filtering capabilities
// ---------------------------------------------------

$filters["custom"]["exampleFilter"] = array("parent,artist_category","exampleFilter");
// Variable: $filters["custom"]["exampleFilter"]
// Add the filter exampleFilter to the custom filters
// list with the source pagetitle and the callback
// exampleFilter
if (!function_exists("exampleFilter")) {
// wrap functions in !function_exists statements to ensure that they are not defined twice

// ---------------------------------------------------
// Function: exampleFilter
//
// Takes the resource array for an individual document
// and asks for the return of a 0 or 1 with 0 removing
// the document and 1 leaving it in the result set.
// In this case, if the lower case value of the pagetitle
// is foo, it is removed while all other documents are shown
// ---------------------------------------------------
function exampleFilter($resource) {
global $modx;
$id = $modx->documentObject['id'];
if (!stristr($resource['artist_category'], $id) && $resource['parent'] != $id) {
return 0;
} else {
return 1;
}
}
}

?>

(если не менять название этого файла, то в вызове Ditto теперь достаточно прописать &extenders=`example`)

Напомню, этот фильтр проверяет отсутствие упоминания id категории в дополнительном параметре artist_category и неравенство родителя этому же id категории, если оба условия верны - документ отфильтровывается. В противном случае документ выводится на страницу. Таким образом, расставив галочки в нашем TV artist_category, мы можем "подцепить" документы из других папок к выводу документов заданной папки :)

четверг, 13 марта 2008 г.

проверка на наличие привязаных документов

Дано: архив артистов, архив новостей и фоторепортажей, привязанных к артистам по идентификатору. Идентификатор артиста записан в соответствующем TV новостной статьи

Задача: выполнить проверку, существуют ли по данному артисту новости (хотябы одна) и вывести соответствующую кнопку на странице артиста и в каталоге артистов (шаблон Ditto)

Решение: нам всего-то нужно - проверить, есть ли в таблице modx_site_tmplvar_contentvalues запись со значением поля value равным идентификатору артиста и принадлежит ли соответствующий документ к категории новостей. Как известно, значения TV хранятся в БД MODx в одной таблице (modx_site_tmplvar_contentvalues), содержимое документов в другой (modx_site_content), а получить значение parent (для проверки принадлежности документа к новостям) мы можем только из таблицы содержимого. Поэтому здесь, для получения и проверки результата мы должны получить "объединенную" выдачу двух таблиц из базы. Для этого запросе к БД используем JOIN:
SELECT contentid, parent FROM modx_site_tmplvar_contentvalues JOIN modx_site_content ON modx_site_tmplvar_contentvalues.contentid = modx_site_content.id WHERE value = ".$output." AND tmplvarid = 5 AND parent IN ('4789','4794','4792')  LIMIT 1

Замечу, что IN проверяет на равенство одному из перечисленных в скобках значений (в моем проекте имеется три категории новостей) а tmplvarid = 5 гарантирует, что проверка производится только в нужном нам TV (привязка к артисту).

Привожу пример работающего phx-сниппета:
<?php
$query = "SELECT contentid, parent, pagetitle FROM modx_site_tmplvar_contentvalues JOIN modx_site_content ON modx_site_tmplvar_contentvalues.contentid = modx_site_content.id WHERE value = ".$output." AND tmplvarid = 5 AND parent IN ('4789','4794','4792') LIMIT 1";
$result = $modx->db->query($query);
$row = mysql_fetch_array($result);
$id = $row['contentid'];
return $id ? '<a class="news" title="Новости" href="[~4817~]?tags=[+pagetitle:name:common_name:url+]">Новости</a>' : '';
?>

я назвал сниппет 'news'. Теперь при вызове [+id:news+] (для шаблона Ditto) и при наличии привязанных новостей к артисту я получаю кнопку/ссылку на новости о нем. Примечание: в моем случае ссылка ведет на выдачу новостей по тегам а не по привязанным статьям - это специфика текущей ситуации и в дальнейшем будет исправлена :)

вторник, 5 февраля 2008 г.

счетчик посещений страницы и настройка рейтинга страниц по нему

Задача: Необходимо считать количество посещений страниц (пока без учета "уникальности" просмотра, то есть грубо говоря посетитель может "накручивать" просмотры, просто перезагружая страницу) и выстроить рейтинг страниц с учетом их посещений.

Решение: Начнем с того, что нам известно - вывод какого либо рейтинга страниц в MODx в штатном порядке осуществляется с помошью Ditto. Ditto, как нам известно, может сортировать вывод по любому полю документа а также по доп. параметру (TV). Единственный выход - получать количество просмотров в TV и потом сортировать по нему.

В репозитарии modxcms.com лежит плагин Page Hit Counter от sottwell, который проверяет просматривает ли страницу менеджер, и не снята ли галочка "Регистрировать посещения" и записывает посещение в свою таблицу БД MODx. Также я вспомнил что в сниппете CSS Star Rating есть замечательная функция setTemplateVar, которая, как вы наверное догадались, записывает значение в TV.

Создаем TV типа "текст" (у меня он называется "count") и делаем гибрид плагина, который вместо записи количества посещений в БД, записывает его в наш TV (не забудьте поставить проверку на системное событие OnWebPagePrerender):
if (!function_exists('setTemplateVar')) {
function setTemplateVar($value, $docID, $tplVarName) {
global $modx;

//-- get tmplvar id
$tplName = $modx->getFullTableName('site_tmplvars');
$tplRS = $modx->db->select('id', $tplName, 'name="' . $tplVarName . '"');
$tplRow = $modx->db->getRow($tplRS);

$tblName = $modx->getFullTableName('site_tmplvar_contentvalues');

$selectQuery = $modx->db->select('*', $tblName, 'contentid=' . $docID . ' AND tmplvarid=' . $tplRow['id']);

$updFields = array (
'value' => $value
);
$insFields = array (
'tmplvarid' => $tplRow['id'],
'contentid' => $docID,
'value' => $value
);

if ($modx->db->getRecordCount($selectQuery) < 1) {
$modx->db->insert($insFields, $tblName);
} else {
$modx->db->update($updFields, $tblName, 'contentid=' . $docID . ' AND tmplvarid=' . $tplRow['id']);
}
}
}

if(!isset($_SESSION['usertype'])) { $_SESSION['usertype'] = ''; }

if($modx->documentObject['donthit'] != 1 && $_SESSION['usertype'] != 'manager') {
$current = $modx->getTemplateVarOutput('count',$modx->documentIdentifier);
$current = $current['count']+1;
@ setTemplateVar("$current", $modx->documentIdentifier, 'count');
}

return;


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

PS. Был бы признателен за дополнение этого плагина, которое бы исключало "накрутку" посещений. Возможно с применением cookie или с сессиями :)

пятница, 1 февраля 2008 г.

теговое облако для 4000 документов

Попробовав применить сниппет TvTagCloud к своей огромной базе документов, получил превышение ожидания запроса (или что то в этом духе). Мне необходимо было выводить определенное количество тегов из недавно созданных документов плюс минимизировать использование ресурсов базы/процессора. Пришлось погуглить и дописать свой код.
<?php
// connect to database

$tb1 = $modx->getFullTableName("site_tmplvar_contentvalues");
$query = "SELECT value";
$query .= " FROM ".$tb1;
$query .= " WHERE tmplvarid=6";
$query .= " GROUP BY value";
$query .= " ORDER BY id DESC";
$query .= " LIMIT 100;";
$result = $modx->db->query($query);

// here we loop through the results and put them into a simple array:

while ($row = mysql_fetch_array($result)) {
$tags = explode(', ',$row['value']);
$tag_mess = array_merge($tag_mess, $tags);
}



// determine the font-size increment
// this is the increase per tag quantity (times used)


$tags = array_count_values($tag_mess);
arsort($tags);
$tags = array_slice($tags, 0, 30);
ksort($tags);

$max_qty = max(array_values($tags));
$min_qty = min(array_values($tags));

// change these font sizes if you will
$max_size = 200; // max font size in %
$min_size = 100; // min font size in %

// get the largest and smallest array values


// find the range of values
$spread = $max_qty - $min_qty;
if (0 == $spread) { // we don't want to divide by zero
$spread = 1;
}
$step = ($max_size - $min_size)/($spread);

// loop through our tag array
foreach ($tags as $key => $value) {
$size = $min_size + (($value - $min_qty) * $step);
$output .= '<a href="/[~4817~]?tags='.urlencode($key).'" style="font-size: '.$size.'%" title="">'.$key.'</a> ';
}
return $output;
?>


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

Сниппет в действии: http://s31183.gridserver.com (не морщитесь - сайт в разработке)

PS. Только сейчас обнаружил еще один сниппет для тегового облака в репозитарии modxcms.com: http://modxcms.com/TagCloud-749.html. Может с моей задачей справился бы и он.