Вместо предисловия
Функциональность подписок на какой-либо контент – одна из востребованных в веб-индустрии. Многие сайты могут похвастаться подобным. И наш проект не стал исключением. Дано: сайт на Drupal 7. Что требуется: найти или написать модуль, реализующий все необходимые функции. Какой вариант был выбран и что из этого вышло, Вы можете прочитать далее.
Поиск подходящих модулей
Самый очевидный и угадываемый вариант – модуль Subscriptions. Данный модуль позволяет пользователям подписываться на изменения нод и таксономии (в том числе, новые комментарии к контенту). Главной причиной отказа от этого варианта послужило отсутствие возможности подписки без перезагрузки страницы и ориентированность модуля в основном на email-рассылки.
Другой вариант – модуль Notifications. В принципе, его расширяемость и масштабируемость нам подходили (для создания своих типов подписок), но отсутствие стабильной версии под 7.x не позволило нам его использовать.
Итак, решение оставалось только одно – написать модуль самим.
Первоначальная настройка
Полностью описывать весь модуль не имеет смысла (это точно выйдет за пределы темы статьи), поэтому опишу основной функционал и начну с основных моментов: настройки админки. Во-первых, необходимо создать тип контента (в моём примере – это Item) с некоторым количеством полей. Здесь и далее основное внимание будет уделяться полям Title, Body, Image.
Во-вторых, следующий этап – создание Views для вывода нод данного типа. К примеру, как на следующем рисунке:
Помимо описанных ранее полей в данной Views выводится также Nid. Для чего же это требуется? Темизировав это поле (к которому впоследствии будет привязано событие подписки), мы можем настроить его вывод по своему усмотрению.
Файлы модуля и их описание
Чтобы начать разработку модуля, создадим в sites/all/modules папку custom_subscriptions. Нам понадобятся 4 файла: custom_subscriptions.info (основная информация о модуле), custom_subscriptions.install (настройки инсталляции), custom_subscriptions.module (собственно, сам файл модуля) и custom_subscriptions.js (в скрипте будет реализован механизм подписки без перезагрузки страницы). Теперь о каждом из них подробнее.
Инфо-файл
Содержимое файла custom_subscriptions.info:
name = "Custom Subscriptions"
description = "Allows users to follow content"
core = 7.x
version = 7.x-1.0
php = 5.1
scripts[] = custom_subscriptions.js
Как видим, файл скрипта был подключён именно здесь.
Файл инсталляции
Для примера, размещённого здесь, будет достаточно одной таблицы в базе данных, содержащей поля uid (идентификатор пользователя) и nid (идентификатор ноды, на которую пользователь подписан).
Содержимое файла custom_subscriptions.install:
<?php
/**
* Implements hook_schema()
*/
function custom_subscriptions_schema() {
$schema['custom_subscriptions'] = array(
'description' => t('The base table for subscriptions'),
'fields' => array(
'sid' => array(
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE,
),
'uid' => array(
'type' => 'int',
'length' => 10,
'not null' => TRUE,
),
'nid' => array(
'type' => 'int',
'length' => 10,
'not null' => TRUE,
),
),
'primary key' => array('sid'),
);
return $schema;
}
После инсталляции таблица будет иметь следующую структуру:
Настройка темы
Прежде всего, следует создать шаблон для вывода Nid во Views. В нашем случае он имеет имя views-view-field--items--page--nid.tpl.php (имя для шаблона можно получить в разделе Advanced / Theme в настройках Views). Файл шаблона размещается в теме (была использована подтема Zen под названием STARTERKIT), в папке templates.
Содержимое файла views-view-field--items--page--nid.tpl.php:
<?php global $user; ?>
<?php if ($user->uid != 0): ?>
<?php $following = db_select('custom_subscriptions', 'cs')
->fields('cs')
->condition('uid', $user->uid)
->condition('nid', $output)
->execute()
->rowCount();
?>
<?php if ($following == 0):?>
<?php print('<a href="follow/' . $output . '" class="follow">FOLLOW</a>'); ?>
<?php else:?>
<?php print('<a href="unfollow/' . $output . '" class="following">FOLLOWING</a>'); ?>
<?php endif; ?>
<?php endif; ?>
Как видно из представленного кода, поле подписок доступно только для авторизированных пользователям. Кроме того, в зависимости от наличия записи в таблице custom_subscriptions, заголовок поля меняется с FOLLOW (если пользователь ещё не подписан на ноду) на FOLLOWING (если подписка уже имеется). Информация о совершаемом действии (follow/unfollow) и идентификатор ноды nid будут храниться в атрибуте href.
Разработка механизма подписки
Так как функциональность файла модуля и скрипта взаимосвязаны, они будут рассмотрены в совокупности.
Начнём с визуальных эффектов: в файле скрипта добавим появление поля подписок по наведению на контент, а также изменение текста надписи с FOLLOWING на UNFOLLOW при наведении на это поле (так же, как это осуществлено в Twitter).
Содержимое файла custom_subscriptions.js:
(function ($) {
Drupal.behaviors.collectiveMove = {
attach: function(context, settings) {
// show buttons on hover
$('.view-items .views-row').hover(function() {
$(this).find('.views-field-nid a').css('display', 'block');
});
// hide buttons on mouseleave
$('.view-items .views-row').mouseleave(function() {
$(this).find('.views-field-nid a.follow, .views-field-nid a.unfollow').css('display', 'none');
});
// show 'unfollow' when user has already had a subscription
$('.view-items .views-row .views-field-nid .following').live('mouseover', function() {
$(this).text('UNFOLLOW');
$(this).addClass('unfollow');
});
// hide 'unfollow' when user has already had a subscription
$('.view-items .views-row .views-field-nid .following').live('mouseout', function() {
$(this).text('FOLLOWING');
$(this).removeClass('unfollow');
});
}
};
})(jQuery);
Теперь начинается самое интересное – передача данных между файлом модуля и скриптом. Основная идея в следующем: при нажатии на FOLLOW post-запрос передаёт данные о действии ('follow') и идентификаторе ноды в модуль, где полученные данные записываются в базу. Для UNFOLLOW механизм сходен, но действие будет другим ('unfollow'), и полученные данные будут удаляться из таблицы базы данных.
Для начала создадим страницу, на которую будет отправляться запрос, используя hook_menu.
Содержимое файла custom_subscriptions.module:
<?php
/*
Implements hook_menu()
*/
function custom_subscriptions_menu() {
$items = array();
$items['custom_subscriptions_ajax'] = array(
'title' => 'Ajax callback',
'page callback' => 'custom_subscriptions_ajax',
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
return $items;
}
Напоминаю, что по правилам оформления кода модулей для Drupal, ставится только открывающий тег php.
Страница готова – добавим функционал.
Содержимое файла custom_subscriptions.module:
<?php
/*
Implements hook_menu()
*/
function custom_subscriptions_menu() {
...
}
/**
* Implements ajax callback function
*/
function custom_subscriptions_ajax() {
global $user;
if($_POST['type'] == 'follow') {
if ($_POST['nid']) {
db_insert('custom_subscriptions')
->fields(array(
'uid' => $user->uid,
'nid' => $_POST['nid'],
))
->execute();
print(1);
}
}
else if($_POST['type'] == 'unfollow') {
if ($_POST['nid']) {
db_delete('custom_subscriptions')
->condition('uid', $user->uid)
->condition('nid', $_POST['nid'])
->execute();
print(1);
}
}
else {
print(0);
}
}
В зависимости от полученного из post-запроса действия (follow/unfollow), добавляем либо удаляем запись из таблицы. Каждая запись идентифицируется по uid (берём идентификатор глобального юзера) и nid. В случае успеха печатаем '1'.
Теперь осталось самое важное – передать данные по нажатию на поле nid на странице, сформированной с помощью Views.
Содержимое файла custom_subscriptions.js:
(function ($) {
Drupal.behaviors.collectiveMove = {
attach: function(context, settings) {
...
// click on follow/unfollow buttons
$('.view-items .views-row .views-field-nid a').bind('click', function(e) {
e.preventDefault();
var href = $(this).attr('href');
var strs = href.split('/');
var item = $(this);
$.post(
Drupal.settings.basePath + 'custom_subscriptions_ajax',
{
type: strs[0],
nid: strs[1],
},
function (data) {
if (data == 1) {
if (strs[0] == 'follow') {
item.text('UNFOLLOW');
item.removeClass('follow');
item.addClass('following unfollow');
item.attr('href', 'unfollow/' + strs[1]);
}
if (strs[0] == 'unfollow') {
item.text('FOLLOW');
item.removeClass('following');
item.removeClass('unfollow');
item.addClass('follow');
item.attr('href', 'follow/' + strs[1]);
}
}
}
);
});
}
};
})(jQuery);
При нажатии на ссылку в поле nid мы проходим следующие шаги:
- запрет выполнения действия по умолчанию (чтобы предотвратить перезагрузку страницы);
- получение действия и nid из атрибута href, в который они были записаны в шаблоне;
- передача полученных данных на подготовленную в модуле страницу;
- после получения успешного результата меняем название и некоторых атрибуты выводимой ссылки.
Вместо послесловия
В данной статье был описан процесс создания простейшего модуля подписок на ноды в Drupal 7. Основным моментом можно назвать осуществление подписок без перезагрузки страницы, используя post-запрос.
На готовый модуль можно взглянуть здесь: github.com/Sacret/custom_subscriptions