Студия разработки сайтов и приложений

Netspark.ru

Заметки и разработки

OctoberCMS

Поковыряемся в модуле Gravatar integration

GravatarКак многие знают, есть в интернетах сервис Gravatar, сопоставляющий пользовательские email-ы с аватарками. Благодаря данному сервису даже анонимы могут иметь собственный аватар, всего лишь указав email рядом с вновь написанным комментарием. Также многим известно, что Gravatar легко и непринужденно поддерживается известной блогомерзкой CMS Wordpress через соответствующий плагин. Есть такой модуль и для Друпала. И называется он Gravatar integration.

Настроить этот модуль для использования граватарок в комментариях — несложно. Этот процесс подробно описал xandeadx в статье «Интеграция Gravatar с помощью одноимённого модуля», так что останавливаться на нем не буду. Далее — о другом.

Модуль Gravatar я хотел поставить уже давно. Люблю я грешным делом все эти аватарки красивые. Но мешало мне то, что также я люблю блок последних комментариев (см. слева). В нем юзерпики загружаются через Views и уменьшаются через imagecache. А поставив Gravatar несколько месяцев назад, я обнаружил отсутствие какой-либо интеграции с Views. Это значит, в блоке будут одни аватарки, а в комментариях и на форуме — другие. Что, конечно, неприятно. Пришлось тогда от модуля отказаться.

А сегодня, исполненный решимости, поставил Gravatar еще раз — с целью добавить загрузку граватарок прямо в шаблоне темизации блока для всех пользователей, не установивших себе обычный юзерпик. Тем более, в последних новостях модулей проходил модуль Imagecache external, позволяющий использовать imagecache для обработки изображений из внешних источников — его тоже хотелось опробовать.

Для начала, полез в код модуля и обнаружил нужную функцию:

/**
 * Generate a gravatar URL.
 *
 * @param $mail
 *   A string with an e-mail address.
 * @param $options
 *   An associative array of additional options, with the following keys:
 * — 'default'
 *     A string with the default gravatar image parameter. Defaults to the
 *     result of _gravatar_get_default_image() with the current value of the
 *     gravatar_default variable.
 * — 'size'
 *     An integer of the desired size of the image. Defaults to smallest size
 *     of the user_picture_dimensions variable.
 * — 'rating'
 *     A string with a MPAA rating limit for the image. Can be 'G', 'PG', 'R',
 *     or 'X'. Defaults to 'G'.
 * — 'cache'
 *     A boolean if TRUE, the resulting image will be cached. Defaults to FALSE.
 *     This feature is not yet implemented.
 * @return
 *   An URL-encoded string with the gravatar image.
 */
function gravatar_get_gravatar($mail, $options = array()) {
  static $is_https;
  if (!isset($is_https)) {
    $is_https = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on';
  }
  // Merge default options.
  $options += array(
    'default' => _gravatar_get_default_image(gravatar_var('default')),
    'size' => _gravatar_get_size(),
    'rating' => variable_get('gravatar_rating', 'G'),
    'cache' => FALSE,
    'force default' => FALSE,
  );
  $hash = md5(drupal_strtolower($mail));
  $gravatar = $is_https ? variable_get('gravatar_url_ssl', GRAVATAR_URL_SSL) : variable_get('gravatar_url', GRAVATAR_URL);
  $gravatar .= $hash . '.jpg';
  $query = array(
    'd' => $options['default'],
    's' => $options['size'],
    'r' => $options['rating'],
    'f' => $options['force default'] ? 'y' : '',
  );
  $query = array_filter($query);
  return url($gravatar, array('query' => $query));
}

Что мы видим из этого кода помимо того, что кэш изображений еще не реализован? Мы видим, что функция gravatar_get_gravatar() принимает два аргумента:

  • $mail — строку, содержащую email пользователя;
  • $options — массив опций получения граватарки.

В опциях передаются:

  • size — размер изображения (все граватарки — квадратные);
  • default — изображение по умолчанию, которое будет показано при отсутствии граватарки (о чем далее);
  • rating — рейтинг MPAA (на случай если вы хотите не показывать матерных и неприличных аватарок);
  • force default — принудительный вывод изображения по умолчанию.

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

По идее, нас устраивают дефолтные установки. Значит, надо (предварительно настроив imagecache external) получить граватарку и вывести ее примерно так:

$picture = gravatar_get_gravatar($mail, array());
print '<img>';
//thumbnails — мой пресет imagecache для уменьшения юзерпиков

И этот код работает. Но только до тех пор, пока мы попробуем получить граватарку для пользователя, у которого ее нет.

Дело в том, что тогда Gravatar возвращает то, что указано в опции default, а точнее — либо одну из трех локально сохраненных дефолтных картинок, либо рандомную граватарку (монстрики, рожицы, абстракции, квадратики), либо логотип сервиса. И оказывается, у imagecache external не получается пропустить через себя дефолтные картинки. В результате все они заменяются логотипом. Из-за этого imagecache external здесь не подходит.

Решил вручную добавить размер в опции — пусть Gravatar сам уменьшает изображения:
Граватарки немедленно стали отображаться как надо.

$options = array(
  'size' => 32,
);
gravatar_get_gravatar($mail, array());
print '<img src="'picture'">';

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

Впрочем, ближайшее рассмотрение показало, что все рандомные граватарки — не такие уж и рандомные, а даже наоборот — одинаковые. Внимательное изучение приведенного выше кода функции gravatar_get_gravatar() показало, что для получения картинки используется md5-хэш аргумента $mail. Очевидно, если разные пользователи не ввели имейлы, аргумент $mail для них будет одинаково пустым. И хэш, соответственно, будет одинаковым тоже. Потому они получат одну и ту же рандомную граватарку.

В то же время при выводе комментариев эти пользователи получают разные граватарки. Почему? Потому что модуль Gravatar integration манипулирует аргументом для генерации разных хэшей для разных анонимных пользователей. Вот часть кода функции прямо перед вызовом gravatar_get_gravatar():

$mail = $account→mail;
$options = array();
if (empty($mail)) {
  $options['force default'] = TRUE;
  // Use various fallbacks to provide a unique default gravatar.
  if (!empty($account→hostname)) {
    $mail = $account→hostname;
  }
  elseif (!empty($account→homepage)) {
    $mail = $account→homepage;
  }
  else {
    $mail = serialize($account);
  }
}
return gravatar_get_gravatar($mail, $options);

Мы видим, что модуль изо всех сил цепляется за всякую возможность сгенерировать для пользователя уникальный хэш. Если нет email-а, используется IP, если нет IP — используется сайт пользователя, если и сайт не был введен — используется сам сериализованный объект $account.

Кажется, это достаточно умно и нужно просто имитировать поведение модуля, считав homepage и hostname комментатора из базы данных и добавив аналогичный код в наш шаблон. Однако проблема в том, что модуль Gravatar integration не читает ничего из БД, а работает с переменными шаблона и, следовательно, хэш зависит от правил их генерации. Так, например, если просматривающий страницу пользователь не имеет права на просмотр IP, hostname в переменных шаблона сгенерирован не будет. А если имеет — будет. То есть разные пользователи будут видеть для одного комментария разные граватарки. Кроме того, в определенных условиях (я так и не смог разобраться, в каких), шаблон считывает граватарку для одного пользователя дважды — сначала по email-у, а потом — по homepage.

Если бы все эти извращения были выделены автором в отдельную функцию генерации хэша (или хотя бы в прозрачное описание порядка действий), жить было бы гораздо проще. А так — у меня получилось сымитировать поведение модуля только частично. Вероятно, нужно поговорить с автором на предмет большей прозрачности генерации хэша для аватарок. Или на предмет интеграции с Views — что даже практичнее. Пока же буду пользоваться описанным решением.

Благо, помимо показа в некоторых случаях разных рандомных картинок в блоке и в соответствующем комменте, все остальное работает нормально.

Комментарии