Песочница →
Личные сообщения в MODx Revolution
Задумали мы с другом один сервис, решили реализовать на MODx Revolution. Причины такого решения, равно как и сам проект лежат далеко за рамками этой статьи, возможно (читай обязательно) я напишу об этом позже. Сегодня я хочу поведать, как решалась конкретная задача.
Итак, необходимо реализовать “социальный” элемент в виде личных сообщений юзеров. Поиски готовых дополнений для MODx ничего толкового не дали, как и гугление на эту тему. Правда, некие проблески все-таки были, но явно не в том направлении. Ну совсем не хотелось использовать ресурсы (которые документы) не по назначению. И тут я обратил внимание на то, что в самом MODx, что называется “из коробки”, уже реализована система сообщений, с одним маленьким “но”: пользоваться ими можно только в админке, куда пускать юзеров вообще не предполагается. Даже никаких намеков на сниппеты для использования во фронтэнде. Тут-то я и решил копнуть глубже.
Итак, что мы имеем. База данных MODx содержит таблицу modx_user_messages и соответствующий xPDO-класс modUserMessage. В принципе, ничто не мешает нам их использовать для нашей системы сообщений, что мы и сделаем.
Для доступа к классу используются методы getObject, getCollection, newObject и другие, в зависимости от того, что нам нужно сделать.
Да, и пару слов о конфиденциальности сообщений, все-таки они личные и кроме адресата не должны быть видны никому. Поэтому сначала придется настроить на нашем сайте авторизацию (в репозитории MODx есть замечательный пакет Login, да и информацию по этой теме найти несложно).
UPD. Не успел я опубликовать статью, как эту тему сегодня же осветили
Пойдем от простого к сложному. Предположим, у нас есть система авторизации и юзеры могут регистрироваться и логиниться в систему. Хорошо, давайте сделаем страницу inbox, на которую будут выводиться входящие сообщения для конкретного пользователя.
Создаем новый ресурс и назначаем ему права доступа только для зарегистрированных пользователей, чтобы незалогиненные “прохожие” даже не подозревали об этой странице. Я опять же не буду подробно останавливаться на этом моменте, ведь статья не об этом.
В MODx в любом чанке или ресурсе доступен плейсхолдер [[+modx.user.id]], который вернет id текущего залогиненного пользователя. Для “прохожих” он вернет ”0”. Его-то мы и будем использовать в качестве адресата (если кто-либо из хабрасообщества знает более надежный способ определения текущего пользователя, с удовольствием перейму его опыт).
Итак, в нашем новом ресурсе разместим вызов сниппета, который мы сейчас напишем:
Обратите внимание, мы передаем в качестве параметра userId id текущего пользователя. Теперь создадим сниппет с названием msg-inbox и добавим в него следующий код:
Как видите, ничего особо выдающегося. Здесь мы вначале принимаем параметры, если таковые будут указаны, или указываем их значения по умолчанию. Потом мы на всякий случай еще раз проверяем id пользователя (вдруг кто-то шаловливыми ручками в ACL забрался), если 0, прекращаем выполнение сниппета. Кстати, при вызове сниппета всегда нужно указывать userId, иначе он по умолчанию принимает значение 0 и сниппет прекращает работу на этом шаге.
Далее мы готовим xPDO к запросу в БД. Нам нужны сообщения, у который хадресат имеет id равный userId. Также мы передаем другие параметры, которые нам понадобятся при постраничном выводе сообщений — $limit и $offset. Ну и сортируем в порядке убывания даты. Строка “$messages = $modx->getCollection('modUserMessage',$c);” делает непосредственно выборку из modUserMessage по критерию $c. После чего проходим по полученному массиву и используем чанк вместо шаблона. Так как при отображении мы по сути читаем сообщения, ставим им статус read равным “1”, т.е. “прочитано”.
Теперь надо создать шаблон для вывода сообщений. Для этого создадим новый чанк с именем “inbox_tpl” и добавим в него следующее:
В принципе, HTML-разметка может быть совершенно любой, на ваш вкус. Поэтому я даже не указываю здесь стили. Единственное, на что хотелось бы обратить внимание, это индикация непрочитанных сообщений. Я использовал для этого класс, который назначается контейнеру сообщения, если у него значение read равно 0: class=”inbox-message[[+read:isequalto=`0`:then=` unread`]]”. В противном случае класс unread просто не добавится. Таким образом, непрочитанные сообщения можно “подсветить” с помощью CSS. Если хотите, можно выводить текст “не прочитано” или соответствующую иконку.
Теперь протестируем. Регистрируем нового пользователя и логинимся во фронтэнде. Заходим на страницу inbox, и естественно, ничего не видим, так как никто нам пока не написал. Что ж, давайте отправим тестовое сообщение. Идем в админку, меню “Пользователи” -> “Сообщения”. Жмем кнопку “Новое сообщение”, выбираем пользователя, заполняем поля “Тема” и “Сообщения” и отправляем. Теперь вернемся на страницу inbox, и вуаля — перед нами новое непрочитанное сообщение от пользователя admin!
Для полноценного общения недостаточно чтения сообщений, их нужно как-то отправлять. Что ж, добавим на нашу страницу форму отправки сообщения. Откроем наш ресурс inbox и добавим форму:
Опять же все на примитивном уровне. Обрабатывать форму будем с помощью сниппета FormIt. На мой взгляд, удобнейший инструмент обработки форм. Для обработки данных FormIt может вызывать цепочку сниппетов — “хуков” (hooks). Хуки могут выполнять самые различные действия — защиту от спама, валидацию форм и другие. Если какой-либо хук забракует форму (например, при валидации), он вернет false и следующие за ним в цепочке хуки не выполнятся. Плюс можно передать сообщение об ошибке.
Для отправки сообщения мы напишем свой хук и скормим его FormIt. Для начала добавим в ресурс inbox вызов FormIt. Рекомендуется располагать его как можно выше на странице, то есть мы разместим его до формы и вызова сниппета msg-inbox:
Обратите внимание на второй параметр submitVar. В нем мы указываем name кнопки “Отправить”, так FormIt будет точно знать, какую форму на странице и как обрабатывать. Излишне говорить, что форм на странице может быть несколько.
Теперь создадим новый сниппет с именем msg-send и добавим в него следующее:
Здесь мы снова сначала проверяем на анонимность, потом забираем данные из формы. FormIt хранит их в объекте $hook. Получить их можно с помощью метода $hook -> getValue(), а метод $hook -> getValues() вернет массив со всеми данными формы. Далее мы создаем новый объект modUserMessage и заполняем его свойства данными. Мы также добавляем дату отправки сообщения и тип — private=1.
Сохраняем, идем проверять. На данном этапе в поле “Кому” нужно писать id получателя. Попробуем отправить сообщение самому себе. Если вы залогинены во фронтэнде как админ, то id получателя будет равен 1. Заполняем остальные поля и жмем “отправить”. Если все сделали правильно, наше сообщение появится на нашей же странице. Теперь можно попробовать нарегистрировать несколько юзеров и попробовать кидаться сообщениями друг другу.
Я намеренно не стал включать в статью определение пользователя по имени или выбор адресата из списка — все это зависит непосредственно от ваших задач и пожеланий. К тому же я оставляю вам большой простор для творчества, да и чтобы бездумной копипасты было поменьше. А насчет имен пользователей даю подсказочку — юзеры в MODx по сути такие же объекты, только класс их называется modUser.
Эта статья не претендует на полноценное решение, ведь здесь нет ни управления сообщениями, ни сортировки, и прочего. Я всего лишь показал, что расширять функционал MODx кастомными сниппетами не так уж и сложно, как кажется на первый взгляд.
Удачи!
P.S. Благодарю сами знаете за что!
Итак, необходимо реализовать “социальный” элемент в виде личных сообщений юзеров. Поиски готовых дополнений для MODx ничего толкового не дали, как и гугление на эту тему. Правда, некие проблески все-таки были, но явно не в том направлении. Ну совсем не хотелось использовать ресурсы (которые документы) не по назначению. И тут я обратил внимание на то, что в самом MODx, что называется “из коробки”, уже реализована система сообщений, с одним маленьким “но”: пользоваться ими можно только в админке, куда пускать юзеров вообще не предполагается. Даже никаких намеков на сниппеты для использования во фронтэнде. Тут-то я и решил копнуть глубже.
1. Что сделано до нас
Итак, что мы имеем. База данных MODx содержит таблицу modx_user_messages и соответствующий xPDO-класс modUserMessage. В принципе, ничто не мешает нам их использовать для нашей системы сообщений, что мы и сделаем.
Для доступа к классу используются методы getObject, getCollection, newObject и другие, в зависимости от того, что нам нужно сделать.
Да, и пару слов о конфиденциальности сообщений, все-таки они личные и кроме адресата не должны быть видны никому. Поэтому сначала придется настроить на нашем сайте авторизацию (в репозитории MODx есть замечательный пакет Login, да и информацию по этой теме найти несложно).
UPD. Не успел я опубликовать статью, как эту тему сегодня же осветили
2. Учимся читать сообщения
Пойдем от простого к сложному. Предположим, у нас есть система авторизации и юзеры могут регистрироваться и логиниться в систему. Хорошо, давайте сделаем страницу inbox, на которую будут выводиться входящие сообщения для конкретного пользователя.
Создаем новый ресурс и назначаем ему права доступа только для зарегистрированных пользователей, чтобы незалогиненные “прохожие” даже не подозревали об этой странице. Я опять же не буду подробно останавливаться на этом моменте, ведь статья не об этом.
В MODx в любом чанке или ресурсе доступен плейсхолдер [[+modx.user.id]], который вернет id текущего залогиненного пользователя. Для “прохожих” он вернет ”0”. Его-то мы и будем использовать в качестве адресата (если кто-либо из хабрасообщества знает более надежный способ определения текущего пользователя, с удовольствием перейму его опыт).
Итак, в нашем новом ресурсе разместим вызов сниппета, который мы сейчас напишем:
[[!msg-inbox? &userId=`[[+modx.user.id]]`]]
Обратите внимание, мы передаем в качестве параметра userId id текущего пользователя. Теперь создадим сниппет с названием msg-inbox и добавим в него следующий код:
<?php
$output = '';
$outputSeparator = isset($outputSeparator) ? $outputSeparator : "\n";
$userId = isset($userId) ? $userId : 0;
$limit = isset($limit) ? (integer) $limit : 10;
$offset = isset($offset) ? (integer) $offset : 0;
$tpl = isset($tpl) ? $tpl : 'inbox_tpl';
if ($userId == 0) {
return;
}
$c = $modx->newQuery('modUserMessage');
$c->where(array(
'recipient' => $userId
));
$c->sortby('date_sent','DESC');
$c->limit($offset,$limit);
$messages = $modx->getCollection('modUserMessage',$c);
foreach ($messages as $msg){
$msgarray = $msg->toArray();
$output .= $modx->getChunk($tpl, $msgarray) . $outputSeparator;
$msg->set('read',1);
$msg->save();
}
return $output;
Как видите, ничего особо выдающегося. Здесь мы вначале принимаем параметры, если таковые будут указаны, или указываем их значения по умолчанию. Потом мы на всякий случай еще раз проверяем id пользователя (вдруг кто-то шаловливыми ручками в ACL забрался), если 0, прекращаем выполнение сниппета. Кстати, при вызове сниппета всегда нужно указывать userId, иначе он по умолчанию принимает значение 0 и сниппет прекращает работу на этом шаге.
Далее мы готовим xPDO к запросу в БД. Нам нужны сообщения, у который хадресат имеет id равный userId. Также мы передаем другие параметры, которые нам понадобятся при постраничном выводе сообщений — $limit и $offset. Ну и сортируем в порядке убывания даты. Строка “$messages = $modx->getCollection('modUserMessage',$c);” делает непосредственно выборку из modUserMessage по критерию $c. После чего проходим по полученному массиву и используем чанк вместо шаблона. Так как при отображении мы по сути читаем сообщения, ставим им статус read равным “1”, т.е. “прочитано”.
Теперь надо создать шаблон для вывода сообщений. Для этого создадим новый чанк с именем “inbox_tpl” и добавим в него следующее:
<div class=”inbox-message [[+read:isequalto=`0`:then=`unread`]]”>
<p><b>От:</b> [[+sender:userinfo=`username`]] </p>
<p><b>Дата:</b> [[+date_sent]] </p>
<p><b>Тема:</b> [[+subject]] </p>
<p><b>Сообщение:</b>
[[+message]]</p>
</div>
В принципе, HTML-разметка может быть совершенно любой, на ваш вкус. Поэтому я даже не указываю здесь стили. Единственное, на что хотелось бы обратить внимание, это индикация непрочитанных сообщений. Я использовал для этого класс, который назначается контейнеру сообщения, если у него значение read равно 0: class=”inbox-message[[+read:isequalto=`0`:then=` unread`]]”. В противном случае класс unread просто не добавится. Таким образом, непрочитанные сообщения можно “подсветить” с помощью CSS. Если хотите, можно выводить текст “не прочитано” или соответствующую иконку.
Теперь протестируем. Регистрируем нового пользователя и логинимся во фронтэнде. Заходим на страницу inbox, и естественно, ничего не видим, так как никто нам пока не написал. Что ж, давайте отправим тестовое сообщение. Идем в админку, меню “Пользователи” -> “Сообщения”. Жмем кнопку “Новое сообщение”, выбираем пользователя, заполняем поля “Тема” и “Сообщения” и отправляем. Теперь вернемся на страницу inbox, и вуаля — перед нами новое непрочитанное сообщение от пользователя admin!
3. Отправляем сообщения
Для полноценного общения недостаточно чтения сообщений, их нужно как-то отправлять. Что ж, добавим на нашу страницу форму отправки сообщения. Откроем наш ресурс inbox и добавим форму:
<form action="[[*id]]" method="post">
<label>Кому </label><input type="text" name="to" value=""/>
<label>Тема </label><input type="text" name="subj" value=""/>
<label>Сообщение </label><textarea name="msg"></textarea>
<input type="submit" name="send" value="Отправить"/>
</form>
Опять же все на примитивном уровне. Обрабатывать форму будем с помощью сниппета FormIt. На мой взгляд, удобнейший инструмент обработки форм. Для обработки данных FormIt может вызывать цепочку сниппетов — “хуков” (hooks). Хуки могут выполнять самые различные действия — защиту от спама, валидацию форм и другие. Если какой-либо хук забракует форму (например, при валидации), он вернет false и следующие за ним в цепочке хуки не выполнятся. Плюс можно передать сообщение об ошибке.
Для отправки сообщения мы напишем свой хук и скормим его FormIt. Для начала добавим в ресурс inbox вызов FormIt. Рекомендуется располагать его как можно выше на странице, то есть мы разместим его до формы и вызова сниппета msg-inbox:
[[!FormIt? &hooks=`msg-send` &submitVar=`send`]]
Обратите внимание на второй параметр submitVar. В нем мы указываем name кнопки “Отправить”, так FormIt будет точно знать, какую форму на странице и как обрабатывать. Излишне говорить, что форм на странице может быть несколько.
Теперь создадим новый сниппет с именем msg-send и добавим в него следующее:
<?php
$userid = isset($userId) ? $userId: 0;
if ($userid == 0) {
return false; // анонимусы не пройдут
};
$to = $hook->getValue('to');
$message = $hook->getValue('msg');
$subj = $hook->getValue('subj');
$msg = $modx->newObject('modUserMessage');
$msg->fromArray(array(
'sender' => $userid,
'recipient' => $to,
'message' => $message,
'subject' => $subj,
'read' => 0,
'private' => 1,
'date_sent'=> strftime('%Y-%m-%d %T'),
));
$msg->save();
return true;
Здесь мы снова сначала проверяем на анонимность, потом забираем данные из формы. FormIt хранит их в объекте $hook. Получить их можно с помощью метода $hook -> getValue(), а метод $hook -> getValues() вернет массив со всеми данными формы. Далее мы создаем новый объект modUserMessage и заполняем его свойства данными. Мы также добавляем дату отправки сообщения и тип — private=1.
Сохраняем, идем проверять. На данном этапе в поле “Кому” нужно писать id получателя. Попробуем отправить сообщение самому себе. Если вы залогинены во фронтэнде как админ, то id получателя будет равен 1. Заполняем остальные поля и жмем “отправить”. Если все сделали правильно, наше сообщение появится на нашей же странице. Теперь можно попробовать нарегистрировать несколько юзеров и попробовать кидаться сообщениями друг другу.
Заключение
Я намеренно не стал включать в статью определение пользователя по имени или выбор адресата из списка — все это зависит непосредственно от ваших задач и пожеланий. К тому же я оставляю вам большой простор для творчества, да и чтобы бездумной копипасты было поменьше. А насчет имен пользователей даю подсказочку — юзеры в MODx по сути такие же объекты, только класс их называется modUser.
Эта статья не претендует на полноценное решение, ведь здесь нет ни управления сообщениями, ни сортировки, и прочего. Я всего лишь показал, что расширять функционал MODx кастомными сниппетами не так уж и сложно, как кажется на первый взгляд.
Удачи!
P.S. Благодарю сами знаете за что!
17.11.2011 13:48+0400