понедельник, 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. Может с моей задачей справился бы и он.

среда, 16 января 2008 г.

сортировка файлов по алфавиту в FCK-менеджере ресурсов

Очень и очень долго рыл по этому вопросу форум и ничего не нашел. Папки и файлы в менеджере ресурсов FCK-Editor (который стоит сейчас по умолчанию как менеджер файлов для TV и прочего) выводятся в соответствии с установками сервера на котором хостится сайт. В большинстве случаев сортировка идет по дате последнего изменения. Когда этих папок становится много, приятней было бы сортировать по алфавиту.

Повторюсь, что на форуме нашел много постов с этим же вопросом, но ни одного вменяемого ответа. Попробовал сам подредактировать mcpuck-коннектор - не осилил.

И вот, мои поиски увенчались успехом - отличное изящное решение от немецкого сообщества MODx (распакуйте архив в корень сайта)

задание шаблона по умолчанию для дочерних документов определенного родителя

Очень давно меня беспокоила необходимость постоянно задавать шаблон для новых документов. Когда речь не идет о периодической информации - шаблон задать нетрудно, однако когда необходимо создавать по нескольку документов в минуту - вопрос стоит достаточно остро. К тому же, ваш заказчик, сам администрируя сайт, может просто забыть выставить нужный шаблон.

В последних версиях MODx появился плагин Inherit Parent Template, с помощью которого можно было создать неопубликованный контейнер и задать ему нужный шаблон, тогда все дочерние документы наследовали этот шаблон. Однако этот способ вынуждал делать лишние контейнеры для каждой категории и создавать иерархию меню отдельно от документов контента.

Итак, хочу представить более менее приемлемое решение. Мы немного изменим вышеупомянутый плагин, чтобы задать родительские документы, новые(!) дочерние документы которых будут автоматически принимать заданный шаблон. Сразу оговорюсь, в моем случае пришлось задавать суперродителей (родителей родителей), однако закомментировав одну строку плагина вы можете иметь возможность задавать просто родителей.

Код нового плагина "Inherit Parent Template" (его можно просто заменить поверх старого):
/*
* Inherit Template from Parent
* Written By Raymond Irving - 12 Oct 2006
*
* Simply results in new documents inherriting the template
* of their parent folder upon creating a new document
*
* Configuration:
* check the OnDocFormPrerender event
*
* Version 1.0
*
*/



global $content;
$e = &$modx->Event;

$parents_and_templates = array (
'4818' => '5', // id родителя => id шаблона (можно посмотреть в БД)
'4817' => '6'
);

switch($e->name) {
case 'OnDocFormPrerender':
if(($_REQUEST['pid'] > 0) && ($id == 0)) {
$parent = $modx->getPageInfo($_REQUEST['pid'],0,'id, parent, template');
$parent = $modx->getPageInfo($parent['parent'],0,'id, parent, template'); //закомментируйте эту строку, если вам нужен родитель первого уровня
$pid = $parent['id'];
if (array_key_exists($pid,$parents_and_templates)) {
$content['template'] = $parents_and_templates[$pid];
}
else {
$content['template'] = $parent['template'];
}
}
break;

default:
return;
break;
}

задать родительский документ и соответствующий шаблон можно в массиве
$parents_and_templates = array (
'4818' => '5',
'4817' => '6'
);

понедельник, 14 января 2008 г.

добавление поля редактирования даты создания документа (createdon) в менеджере

Одной из проблем, с которыми приходилось сталкиваться не раз - запись даты создания документа задним числом. Можно создавать TV типа date, можно записывать даты в pub_date и тд. Однако, если вы не задаете абсолютно всем документам дату в своем параметре и для каких то документов он остается пустым - возникнут проблемы с сортировкой этих документов с помощью Ditto.

Вот захотелось набраться наглости и сделать в панели редактирования документа дополнительное поле, позволяющее редактировать дату создания документа (createdon). Создавая документ задним числом, вы просто редактируете дату его создания, а по умолчанию естественно задается текущая дата. Итак нужно выполнить следующие шаги:
  1. в manager/actions/mutate_content.dynamic.php меняем
    // retain form values if template was changed
    // edited to convert pub_date and unpub_date
    // sottwell 02-09-2006
    if ($formRestored == true || isset ($_REQUEST['newtemplate'])) {
    $content = array_merge($content, $_POST);
    $content["content"] = $_POST["ta"];
    if (empty ($content["pub_date"])) {
    unset ($content["pub_date"]);
    } else {
    $pub_date = $content['pub_date'];
    list ($d, $m, $Y, $H, $M, $S) = sscanf($pub_date, "%2d-%2d-%4d %2d:%2d:%2d");
    $pub_date = strtotime("$m/$d/$Y $H:$M:$S");
    $content['pub_date'] = $pub_date;
    }
    if (empty ($content["unpub_date"])) {
    unset ($content["unpub_date"]);
    } else {
    $unpub_date = $content['unpub_date'];
    list ($d, $m, $Y, $H, $M, $S) = sscanf($unpub_date, "%2d-%2d-%4d %2d:%2d:%2d");
    $unpub_date = strtotime("$m/$d/$Y $H:$M:$S");
    $content['unpub_date'] = $unpub_date;
    }
    }
    на
    // retain form values if template was changed
    // edited to convert pub_date and unpub_date
    // sottwell 02-09-2006
    if ($formRestored == true || isset ($_REQUEST['newtemplate'])) {
    $content = array_merge($content, $_POST);
    $content["content"] = $_POST["ta"];
    if (empty ($content["pub_date"])) {
    unset ($content["pub_date"]);
    } else {
    $pub_date = $content['pub_date'];
    list ($d, $m, $Y, $H, $M, $S) = sscanf($pub_date, "%2d-%2d-%4d %2d:%2d:%2d");
    $pub_date = strtotime("$m/$d/$Y $H:$M:$S");
    $content['pub_date'] = $pub_date;
    }
    if (empty ($content["unpub_date"])) {
    unset ($content["unpub_date"]);
    } else {
    $unpub_date = $content['unpub_date'];
    list ($d, $m, $Y, $H, $M, $S) = sscanf($unpub_date, "%2d-%2d-%4d %2d:%2d:%2d");
    $unpub_date = strtotime("$m/$d/$Y $H:$M:$S");
    $content['unpub_date'] = $unpub_date;
    }

    if (empty ($content["createdon"])) {
    unset ($content["createdon"]);
    } else {
    $createdon = $content['createdon'];
    list ($d, $m, $Y, $H, $M, $S) = sscanf($createdon, "%2d-%2d-%4d %2d:%2d:%2d");
    $createdon = strtotime("$m/$d/$Y $H:$M:$S");
    $content['createdon'] = $createdon;
    }
    }

  2. добавляем в manager/actions/mutate_content.dynamic.php до строки 700(приблизительно, строка содержит <input name="pub_date">
    <tr>
    <td><span class="warning">Дата создания</span></td>
    <td>
    <?php
    $timestamp = time();
    ?>
    <input name="createdon" value="<?php echo $content['createdon']=="0" || !isset($content['createdon']) ? strftime("%d-%m-%Y %H:%M:%S", $timestamp) : strftime("%d-%m-%Y %H:%M:%S", $content['createdon']); ?>" onblur="documentDirty=true;" />
    <a onclick="documentDirty=false; cal0.popup();" onmouseover="window.status='<?php echo $_lang['select_date']; ?>'; return true;" onmouseout="window.status=''; return true;" style="cursor:pointer; cursor:hand"><img src="media/style/<?php echo $manager_theme ? "$manager_theme/":""; ?>images/icons/cal.gif" width="16" height="16" border="0" alt="<?php echo $_lang['select_date']; ?>" /></a>
    </td>
    </tr>
  3. добавляем в manager/actions/mutate_content.dynamic.php в самый конец файла, в после строки
    <script type="text/javascript">
    ... следующий код:
      var cal0 = new calendar1(document.forms['mutate'].elements['createdon'], document.getElementById("pub_date_show"));
    cal0.path="<?php echo str_replace("index.php", "media/", $_SERVER["PHP_SELF"]); ?>";
    cal0.year_scroll = true;
    cal0.time_comp = true;
  4. добавляем в manager/processors/save_content.processor.php после строки "$hidemenu = intval($_POST['hidemenu']);":
    $createdon = $_POST['createdon'];
    list ($d, $m, $Y, $H, $M, $S) = sscanf($createdon, "%2d-%2d-%4d %2d:%2d:%2d"); //added by yentsun
    $createdon = mktime($H, $M, $S, $m, $d, $Y);
  5. в manager/processors/save_content.processor.php меняем sql-запрос, начинающийся с INSERT на
    $sql = "INSERT INTO $tblsc (introtext,content, pagetitle, longtitle, type, description, alias, link_attributes, isfolder, richtext, published, parent, template, menuindex, searchable, cacheable, createdby, createdon, editedby, editedon, publishedby, publishedon, pub_date, unpub_date, contentType, content_dispo, donthit, menutitle, hidemenu)
    VALUES('" . $introtext . "','" . $content . "', '" . $pagetitle . "', '" . $longtitle . "', '" . $type . "', '" . $description . "', '" . $alias . "', '" . $link_attributes . "', '" . $isfolder . "', '" . $richtext . "', '" . $published . "', '" . $parent . "', '" . $template . "', '" . $menuindex . "', '" . $searchable . "', '" . $cacheable . "', '" . $modx->getLoginUserID() . "', " . $createdon . ", '" . $modx->getLoginUserID() . "', " . time() . ", " . $publishedby . ", " . $publishedon . ", '$pub_date', '$unpub_date', '$contentType', '$contentdispo', '$donthit', '$menutitle', '$hidemenu')";
    , блок
    $sql = "UPDATE $tblsc SET introtext='$introtext', content='$content', pagetitle='$pagetitle', longtitle='$longtitle', type='$type', description='$description', alias='$alias', link_attributes='$link_attributes',
    isfolder=$isfolder, richtext=$richtext, published=$published, pub_date=$pub_date, unpub_date=$unpub_date, parent=$parent, template=$template, menuindex='$menuindex',
    searchable=$searchable, cacheable=$cacheable, editedby=" . $modx->getLoginUserID() . ", editedon=" . time() . ", publishedon=$publishedon, publishedby=$publishedby, contentType='$contentType', content_dispo='$contentdispo', donthit='$donthit', menutitle='$menutitle', hidemenu='$hidemenu' WHERE id=$id;";
    на
    $sql = "UPDATE $tblsc SET introtext='$introtext', content='$content', pagetitle='$pagetitle', longtitle='$longtitle', type='$type', description='$description', alias='$alias', link_attributes='$link_attributes',
    isfolder=$isfolder, richtext=$richtext, published=$published, pub_date=$pub_date, unpub_date=$unpub_date, parent=$parent, template=$template, menuindex='$menuindex',
    searchable=$searchable, cacheable=$cacheable, editedby=" . $modx->getLoginUserID() . ", editedon=" . time() . ", publishedon=$publishedon, publishedby=$publishedby, contentType='$contentType', content_dispo='$contentdispo', donthit='$donthit', menutitle='$menutitle', hidemenu='$hidemenu', createdon='$createdon' WHERE id=$id;";
    (добавляем в хвост запроса строку createdon='$createdon')


-----------------

Хотите запечатлеть важнейшие моменты в жизни? Воспользуйтесь свадебной фотографией Марины Салмановой.

четверг, 10 января 2008 г.

выод лимитированного количества теговых ссылок

Задача: есть некое количество документов, в которых слишком много тегов. Это конечно не нормальная ситуация, однако предположим что такие документы есть и нам нужно выводить тегов не более заданного числа

Решение: плейсхолдер Ditto [+tagLinks+], отвечающий за вывод теговых ссылок, параметра лимитирования не имеет, поэтому нам придется написать свой phx-фильтр:

$options = explode('|', $options);

$delimiter = (!empty($options[0])) ? $options[0] : ', ';
$links_limit = (!empty($options[1])) ? $options[1] : 3;
$url = $options[2];
$links_class = (!empty($options[3])) ? $options[3] : 'tag_link';

$array = explode($delimiter, $output);
$tail = (count($array)>$links_limit) ? '...' : '';
for ($i=0; $i<=$links_limit-1; $i++) {
$retval .= '<a class="'.$links_class.'" href="[(base_url)]'.$url.'?tags='.urlencode($array[$i]).'">'.$array[$i].'</a>';
}
return $retval.$tail;

назовем его например phx:taglinks_limited и вызовем следующим образом (в шаблоне Ditto):
[+tags:taglinks_limited=`, |2|[~[*id*]~]|ditto_tag`+]

параметры нашего фильтра разделяются символом "|" и расшифровываются как разделитель тегов|количество выводимых ссылок|url страницы с выводом документов по тегу|CSS-класс теговых ссылок

нюансы перехода от windows-1251 к utf-8

Проблема: после перехода сайта на utf-8 все работает нормально за исключением следующих моментов
  • фраза "без категории" в управлении ресурсами отображается как "??? ?????????"
  • после сохранения кода сниппетов, чанков и тд, все кириллические символы внутри превращаются в вопросы
Причина: не настроена кодировка соединения самого менеджера с базой (или какого то его блока)

Решение: взято из форума. В файле manager/index.php около строки 133...

    mysql_select_db($dbase);
mysql_query("SET NAMES 'utf8';",$modxDBConn);
mysql_query("SET CHARACTER SET 'utf8';",$modxDBConn);
mysql_query("SET SESSION collation_connection = 'utf8_general_ci';",$modxDBConn);
В некоторых случаях придется сделать похожие изменения и в файле manager/includes/extenders/dbapi.mysql.class.inc.php около 90 строки (см. сообщение в форуме)

считывание файла xls с помощью php

Задача: считать информацию из файла Excel в массивы php для последующего вывода в динамический файл javascript.

Решение: отлично справляется бесплатный скрипт PHP-ExcelReader, созданный нашим соотечественником, Вадимом Ткаченко.

Пример калькулятора, работающего от подгружаемого xls-файла можно найти на сайте tclogic.ru