Топики пользователя «antonkuzmin»

Дополнение - социальная сеть на MODx Evo

Добрый день всем.
В процессе разработки одного из проектов появилось большое желание выбросить весь огород из webloginpe+FDM+самописные скрипты, на которых ворочался функционал user-generated content и реализовать с нуля компонент, реализующий некоторый функционал, присущий социальным сетям.
Сейчас пользовательский контент — это ресурсы в дереве modx, находящиеся в определенных папках и со строго определенными шаблонами.

Структура расширения предполагается такой:
— Класс, с экземпляром $modx->social который создается по OnLoadWebPage и доступен всегда и везде, на манер $modx->db.
— Модуль, в котором производится конфигурация этого расширения и в котором можно наблюдать и вмешиваться в пользовательскую активность
— Набор сниппетов, реализующих различные аспекты соц.сети через объект $modx->social (сниппет регистрации, личного кабинета, редактора контента, вывода топа пользователей, и т.д.)

На данный момент (25.11.2011) запланировано и отчасти реализовано:
— Скелет класса
— Настройка политик для различных веб-групп пользователей по созданию/редактированию пользовательского контента
— Методы для отрисовки формы редактирования контента и сохранения с проверкой на соответствие правам текущего веб-пользователя
— Обязательно будут способы управления пользовательскими файлами (некая замена админскому MCPuk для фронтенда)
— Политики для групп пользователей сейчас имеют такой вид:
web-группа => шаблоны, с которыми разрешено создавать документы + описание родительских документов, где пользовательский контент разрешено создавать

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

И еще планируется переключатель — хранить весь пользовательский контент в таблицах modx или в собственных таблицах (именно он позволит создать сайт из сотен тысяч ресурсов, который не будет тормозить :)).

Что очень лень делать:
— списки друзей + настройки приватностей (иными словами функционал Вконтакте)))
— личные сообщения
— управление аватарками
— всевозможные рейтинги пользователей, блогов и кармы
— комментирование и оценка всего и вся (но в минимальном наборе будет сделано)

В этом топике хотелось бы услышать пожелания по функционалу и может быть собрать какие-то наработки, которые позволят ускорить разработку дополнения.
А может соц.сеть на modx вообще никому не нужна, и стоит ограничиться только минимально необходимым фунционалом для user-generated-content?

Заставляем eForm исполнять сниппеты в шаблоне формы

Ситуации, когда требуется вывести какой-либо select в шаблон формы eForm заполнив его данными из базы нередки. Стандартный способ, описанный в документации к eForm — использование &eformOnBeforeFormParse и последующие пляски с бубном.
Если просто вставить вызов сниппета в код формы, то сниппет будет интерпретирован уже после того, как eForm разберет шаблон, и он не будет учтен.

Мне совершенно неочевидно, почему разработчики eForm не позволили использовать шаблонизатор modx для создания формы, но, как оказалось, мы легко можем сделать это сами.


( Читать дальше )

Custom TV Яндекс.Карты



По просьбам выкладываю свое решение по привязке яндекс-карты к tv-параметру, используя возможности modx 1.0.5


( Читать дальше )

Формирование формы для фронт-енд редактирования контента

Сниппет основан на механизмах MODx для формирования страницы редактирования в /manager/ и использует функции из соответствующих файлов. Код сниппета [[fdm_form_render]]:

<?php
global $modx;

// &id
// &tpl
// &hide : 'name_of_tv1,name_of_tv2'
// &add  : 'pagetitle,alias,parent'

if($hide) $hide = explode(',',$hide);
if(!$tpl) $tpl = intval($_GET['tpl']);
if($id && !$tpl)
{
    $tplidc = $modx->db->select('template','modx_site_content','id = '.intval($id));
    $tplidc = $modx->db->makeArray($tplidc);
    $tpl = $tplidc[0]['template'];
} 
    
$tvs = $modx->db->select('tmplvarid,rank','modx_site_tmplvar_templates','templateid = '.intval($tpl),'rank' );
$tvs = $modx->db->makeArray($tvs);
//print_r($tvs);

echo '<tr style="display:none;"><td colspan="2">';
echo '<input type="hidden" name="template" value="'.$tpl.'" /> ';
echo '</td></tr>';

// $new_id = $modx->db->getValue( $modx->db->query('SELECT `id` FROM `modx_site_content` ORDER BY `id` DESC limit 1') );
$r = $modx->db->getRow( $modx->db->query("SHOW TABLE STATUS LIKE 'modx_site_content'") );
$new_id = $r['Auto_increment'];

if(strpos(','.$add.',', ',alias,')!==false) {
  echo '    <tr>
        <td><!-- label for="alias" class="title">Псевдоним</label --></td>
        <td><input name="alias" id="alias" value="'.$new_id.'" type="hidden" /></td>
    </tr>';
}

///////////////////

// Get table names (alphabetical)
$tbl_active_users               = $modx->getFullTableName('active_users');
$tbl_categories                 = $modx->getFullTableName('categories');
$tbl_document_group_names       = $modx->getFullTableName('documentgroup_names');
$tbl_member_groups              = $modx->getFullTableName('member_groups');
$tbl_membergroup_access         = $modx->getFullTableName('membergroup_access');
$tbl_document_groups            = $modx->getFullTableName('document_groups');
$tbl_keyword_xref               = $modx->getFullTableName('keyword_xref');
$tbl_site_content               = $modx->getFullTableName('site_content');
$tbl_site_content_metatags      = $modx->getFullTableName('site_content_metatags');
$tbl_site_keywords              = $modx->getFullTableName('site_keywords');
$tbl_site_metatags              = $modx->getFullTableName('site_metatags');
$tbl_site_templates             = $modx->getFullTableName('site_templates');
$tbl_site_tmplvar_access        = $modx->getFullTableName('site_tmplvar_access');
$tbl_site_tmplvar_contentvalues = $modx->getFullTableName('site_tmplvar_contentvalues');
$tbl_site_tmplvar_templates     = $modx->getFullTableName('site_tmplvar_templates');
$tbl_site_tmplvars              = $modx->getFullTableName('site_tmplvars');

///////////////////

$docgrp = ''; //implode(',', $_SESSION['mgrDocgroups']);

$replace_richtexteditor = array(
                    'ta',
                );

$template = $tpl;

                $sql = 'SELECT DISTINCT tv.*, IF(tvc.value!=\'\',tvc.value,tv.default_text) as value '.
                       'FROM '.$tbl_site_tmplvars.' AS tv '.
                       'INNER JOIN '.$tbl_site_tmplvar_templates.' AS tvtpl ON tvtpl.tmplvarid = tv.id '.
                       'LEFT JOIN '.$tbl_site_tmplvar_contentvalues.' AS tvc ON tvc.tmplvarid=tv.id AND tvc.contentid=\''.$id.'\' '.
                       'LEFT JOIN '.$tbl_site_tmplvar_access.' AS tva ON tva.tmplvarid=tv.id '.
                       'WHERE tvtpl.templateid=\''.$template.'\' ORDER BY tvtpl.rank,tv.rank';
                $rs = mysql_query($sql);
                $limit = mysql_num_rows($rs);
                if ($limit > 0) {
//                    echo "\t".'<table class="fdm_tvs" border="0" cellspacing="0" cellpadding="0">'."\n";
                    require_once(MODX_MANAGER_PATH.'includes/tmplvars.inc.php');
                    require_once(MODX_MANAGER_PATH.'includes/tmplvars.commands.inc.php');
                    for ($i = 0; $i < $limit; $i++) { 
                        // Go through and display all Template Variables
                        $row = mysql_fetch_assoc($rs);
                        if( is_array($hide) && in_array($row['name'],$hide) ) continue;
                        
                        if ($row['type'] == 'richtext' || $row['type'] == 'htmlarea') {
                            // Add richtext editor to the list
                            if (is_array($replace_richtexteditor)) {
                                $replace_richtexteditor = array_merge($replace_richtexteditor, array(
                                    "tv" . $row['id'],
                                ));
                            } else {
                                $replace_richtexteditor = array(
                                    "tv" . $row['id'],
                                );
                            }
                        }
                        // splitter
                        if ($i > 0 && $i < $limit)
                            echo "\t\t",'<tr><td colspan="2"><div class="split"></div></td></tr>',"\n";

                        $tvPBV = array_key_exists('tv'.$row['id'], $_POST) ? $_POST['tv'.$row['id']] : $row['value']; // post back value
                        $field_html = renderFormElement($row['type'], $row['name'], $row['default_text'], $row['elements'], $tvPBV); // , ' style="width:300px;"'
                        
                        echo '<tr><td class="label"><p class="title">',$row['caption'],"</p>\n",
                             '<p class="comment">',$row['description'],"</p></td>\n",
                             '<td class="field">',"\n",
                             $field_html,"\n",
                             "</td></tr>\n";
                    }
//                    echo "\t</table>\n";
                } else {
                    // There aren't any Template Variables
                    echo "Нет tv-параметров\n";
                }
//echo "</table>\n";
?>


Для использования, нужно вызвать сниппет в чанке с шаблоном формы FDM, указав требуемый номер шаблона. Также можно указать tv-параметры, которые нужно исключить из формы:

<form method="post" action="[[get?name=`$uri`]]"  id="fdmForm"> 
<fieldset> 
 
    <input type="hidden" name="formid" value="fdmForm" /> 
    <input type="hidden" name="fdmid" value="[[get?&name=`fdmid`]]" eform="::0::" /> 
...
[[fdm_form_render? &id=`[+id+]` &tpl=`[[get?name=`tplid`]]` &hide=`user_id,icon,mg_type` ]]
...
</fieldset> 
</form> 


Сниппет выводит только tv-параметры! Стандартные, такие как заголовок, аннотацию, дату публикации и т. д. он не возвращает.

UPD: Добавил скриншот личного кабинета одного из сайтов:

Темизация в MODx Evolution: Magic Themes


Хочу рассказать о дополнении к MODx, над которым сейчас работаю. Надеюсь, что с вашей помощью это сможет привести к переосмыслению шаблонизации MODx :)


( Читать дальше )

Сниппет авторизации через Loginza.ru



Предлагаю вашему вниманию сниппет, реализующий регистрацию/авторизацию пользователей через сервис loginza.ru.
Loginza.ru предоставляет единый API для авторизации через OpenID провайдеров + Vkontakte, mail.ru, twitter и т.д.


( Читать дальше )

eForm и кавычки

Столкнулся с проблемой. При вводе в поле типа text строки, содержащей кавычки, на выходе получаем следующее:
<input name="fio" type="text" class="text" value="Строка "с кавычками" - проблема" />

Естественно, все после первой кавычки отсекается. Выходит, eForm не делает никакого экранирования вообще!? Поиск гуглом ничего не дал. Не верю, что никто не сталкивался с этой проблемой.
Конечно, экранирование можно осуществить через сниппет, повешенный на событие eForm, но делать это для каждого проекта вручную — не выход.
Есть идея пропатчить код eForm.

Оптимизация PHx - убираем лишние запросы к БД

На modxcms.com пользователь tobice столкнулся с проблемой чрезмерно большого количества обращений к БД.
В результате выяснилось, что в плагине PHx содержится досадная ошибка, которая приводит к 1 дополнительному запросу к БД при каждом вызове phx-сниппета. К примеру, если вы используете Дитто, то вы получите по N дополнительных запросов на каждую итерацию (в случае некешируемого вызова)!


( Читать дальше )

Подборка моих сайтов на MODx

Решил и я засветить некоторые сайты, разработанные на MODx при моем непосредственном участии.
На многих из них использованы нетривиальные решения, рассказать о которых смогу в комментах.

Во всех сайтах используются: PHx, ManagerManager, MaxiGallery (заточенный под собственные нужды).

Публикую в хронологическом порядке:

1. www.svcn.ru (2009, 0.9.6.3) — первый мой сайт на MODx. Из ноу-хау — простой форум раздел/топик/комментарии реализованный на Jot+Ditto+NewsPublisher. Пока правда так и не запущен заказчиком.

2. biotst.ru (2009, 1.0) — совершенно рядовой сайт. Из особенностей — заложенная многоязычная поддержка, из-за заказчика до сих пор не запущенная.

3. 7nebo-nsk.ru (2009, 1.0.1) — обычный в общем-то сайт, если не считать интересно реализованные разделы О комплексе, Местоположение и Фотогалерея. Во всех трех разделах используется обычный Ditto с необычными шаблонами :)

4. gorod-master.ru (2010, 1.0.2) — тоже ничего необычного, кроме огромного количества шаблонов и различных списков, а также сложной логики отображения. Тут PHx показал свои ограничения, кое-что делалось со скрипом. Также интересен интеграцией с Яндекс-картами в разделе Контакты (все сделано на банальном Ditto, эх люблю я его за гибкость).

5. Сейчас разрабатывается портал по трудоустройству выпускников при отделе маркетинга одного из университетов. Реализована регистрация студентов и компаний, публикация компаниями вакансий, анонсов и прочего. Есть календарь мероприятий, поиск по базе вакансий и еще много мелочей, в том числе для администраторов сайта. Много самописного, систему кеширования пока не затрагивали. Еще больше всего будет сделано (в том числе интеграции с различными сервисами). Пока что MODx не показывает явно своих ограничений, в дальнейшем планирую переводить портал на MODx Revolution.
Если бы брался за такой проект сейчас, выбрал бы Битрикс. Размещу анонс, когда проект будет запущен на продакшне.

phx: согласование числительных

В топике Ditto: вывод количества комментариев к документам за один запрос фигурировал сниппет для phx, который занимается тем, что правильно склоняет существительные, относящиеся к числительным, например «32 комментария, 147 арбузов».

Работает он так:
комментар[+jotcount:declension=`ий|ия|иев`+]
Первое окончание соответствует единственному числу, а также числам, оканчивающимся на цифру 1. Второе окончание используется, когда число оканчивается на 2,3 или 4. Третья форма используется во всех других случаях.

Привожу код сниппета:

<?php
if(strlen($options)>0) {

$data = explode("|",trim($options));
$num = $output;
$res = $data[0];

if($num%10 > 1 && $num%10<5) $res = $data[1];
if($num%10 > 4 || $num%10==0) $res = $data[2];
if($num%100>10 && $num%100<20) $res = $data[2];

return($res);
}
?>


Вполне возможно, что этот сниппет не учитывает каких-то частных случаев, поэтому поправки приветствуются :)