Пользовательский контент

Опубликовано 07.03.2009

Еще со времен запуска первой версии Записок я ношу в себе идею разработки собственного блогодвижка. Задача, казалось бы, не ахти какая сложная и очень многие ее успешно решают, движков понаписано великое множество на всех языках. Собственно, поэтому идея и пролежала в моей голове без дела почти 4 года. И вот буквально после новогодних праздников я наконец нашел достойный повод взяться за нее – блоги практически не представлены на Facebook.

Существует всего несколько приложений под Facebook, относящихся в той или иной степени к блоггингу. Стандартные записки (Notes) убивают всякий коммерческий интерес к разработке альтернатив. И тем не менее, я взялся такую альтернативу создать, в первую очередь ради эксперимента. Получилась вот такая штука: Blog Box (демонстрационный блог)

По ходу работы над приложением выявилось множество нюансов, которые встречаются от проекта к проекту. Одним из первых вопросов, с которыми я столкнулся – как работать с пользовательским контентом.

Разметка

Практически каждый человек хочет как-то разнообразить свои посты, добавить в них ссылки, выделить текст жирным или курсивом, вставить список или табличку. Надо понимать, что большинство пользователей не знают и знать не хотят что такое HTML. В блогоплатформах для пользователей обычно ставится WYSIWYG-редактор типа TinyMCE, FCKEditor, Xinha или любой другой. Однако в Facebook такой вариант не пройдет – платформа очень жестко контролирует JavaScript, используемый приложением, и практически сводит на нет возможность использования готовых библиотек.

Для ручной разметки текста и избавления от необходимости в визуальном редакторе можно использовать упрощенные системы разметки, которые часто применяются в wiki-системах. Для Ruby есть два реализованных формата разметки – Textile (с помощью RedCloth) и Markdown (с помощью BlueCloth). Textile более гибок в плане стилевой разметки, Markdown проще для освоения. Я решил остановиться на Textile.

Обработку текста я вынес в отдельный хелпер, единый как для постов, так и для комментариев:

def format_content(content)
  textilize(content.to_s)
end

Метод textilize – стандартный рельсовый, становится доступен после установки RedCloth. Для обработки постов и комментариев я создал отдельные хелперы, передающие соответствующий контент для форматирования:

def post_content(post)
  format_content(post.text)
end

def post_excerpt(post)
  post.excerpt.blank? ? post_content(post) : format_content(post.excerpt)
end

def comment_content(comment)
  format_content(comment.text)
end

Далее вызываем нужные нам хелперы хоть на странице просмотра поста, хоть при выводе списка постов.

Очистка HTML

Если честно, я ожидал, что мое приложение сразу кто-нибудь попробует использовать в гнусных целях, похакать, засунуть XSS или какой-нибудь мерзкий JavaScript. Однако я не ожидал, что это случится в первый же день жизни приложения. Хорошо, что я озаботился где возможно прикрыть вывод пользовательских данных с помощью хелпера h():

<%=h @post.title %>

Однако текст поста, который форматируется с помощью Textile, так не обезопасишь. Поэтому следующее что пришлось делать – это очистку текста постов от потенциально опасных тэгов и атрибутов. Для решения этой задачи существует несколько различных вариантов. В рельсах есть встроенный хелпер для очистки от ненужных тэгов – sanitize. Есть сторонние решения, такие как xss_terminate, Dryopteris или sanitize, использующие возможности более быстрых библиотек для работы с HTMLHpricot и Nokigiri.

Я решил использовать в своем проекте плагин Sanitize – он для меня оказался более удобен в конфигурировании. Для интеграции с моделями я написал небольшой модуль, в котором немного поменял стандартные схемы очистки тэгов и атрибутов, а также добавил метод sanitize_attributes для быстрого привязывания очистки атрибутов. Весь входящий HTML разбирается средствами Hpricot, вычищаются ненужные тэги и атрибуты, добавляются нужные атрибуты (например, rel=nofollow для ссылок из комментариев) и обработанный код сохраняется в БД.

Специальные тэги

Мое знакомство с блоггингом, как и у многих, началось с LiveJournal. Система во многом неудобная, но в ней есть одна изюминка, которая мне очень нравилась – специальные тэги. Например, если я вставлю в текст поста тэг <lj user="my_friend">, то в тексте поста будет показана красиво оформленная ссылочка на журнал моего друга со всплывающей по наведению мышки информацией. Фишка очень интересная и я обязательно хотел использовать ее в своем блогодвижке.

Что самое смешное, я был даже вынужден ее использовать, когда дело дошло до вставки видео. В Facebook есть суровое ограничение на то, какие тэги можно использовать на страницах, а какие – нет. Это ограничение касается и тэга <object>, который по факту запрещен. Вместо него для вставки Flash используется тэг <fb:swf>, однако он сильно ограничен в плане конфигурирования. Видео-хостинги же для вставки видео дают только код на основе тэга <object>, без вариантов. Я задумался.

Решение нашлось довольно быстро – выводить обычный код вставки видео внутри тэга <iframe>, на который ограничение не накладывается. Однако опять же, код для iframe нужно каким-то образом сформировать, да и сам iframe нужно как-то вставить. Для формирования кода я решил использовать плагин acts_as_unvlogable, позволяющий из URL страницы с видео сформировать код. Вопрос со вставкой iframe встал на первый план.

На помощь пришла библиотека Radius, ключевая фишка Radiant CMS. С ее помощью можно создавать свои тэги, которые затем парсятся и обрабатываются ruby-кодом. Для размещения видео пользователю необходимо сформировать и вставить в текст поста вот такой код:

<bb:video url="http://www.youtube.com/watch?v=v9Lj0lF9Lao"></bb:video>

Обработка radius-кода вынесена в отдельный модуль, упрощающий добавление новых тэгов и их использование. Метод класса define_tag объявляет новый тэг, создает из блока метод экземпляра, который вызывается при обработке тэга, а так же добавляет тэг в общий список спец-тэгов, который можно затем использовать для расширения схемы очистки HTML, используемой sanitizer’ом:

  • Blogbox::RadiusContext.tags_with_namespace – все спецтэги
  • Blogbox::RadiusContext.tag_attributes – все разрешенные атрибуты

Для того, чтобы подключить спец-тэги стразу везде, меняем хелпер форматирования контента и хелперы вывода постов и комментариев:

def format_content(content, assigns = {}, &block)
  textilized_content = textilize(content.to_s)

  @@radius_context ||= Blogbox::RadiusContext.new(&block)
  @@radius_context.set_current_context(self, 
    assigns.merge(
      :view       => self,
      :controller => controller
    )
  )

  Radius::Parser.new(@@radius_context,
    :tag_prefix => Blogbox::RadiusContext::NAMESPACE
  ).parse(textilized_content)
end

def post_content(post)
  format_content(post.text, :post => post)
end

def post_excerpt(post)
  return post_content(post) if post.excerpt.blank? 

  format_content(post.excerpt, :post => post)
end

def comment_content(comment)
  format_content(comment.text, :post => post, :comment => comment)
end

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

Резюме

Пользовательский контент – далеко не банальная вещь. Здесь есть и подводные камни, и простор для творчества, и необходимость в разумном обстоятельном подходе. Если вы работаете с пользовательским контентом – заранее обдумайте, какие возможности вы хотите дать пользователю и какие точно не хотите давать. Исходя из специфики задачи и среды можно использовать те инструменты, которые уже разработаны ruby-сообществом, и сделать свой проект по истине уникальным.

Блог переехал

Опубликовано 27.02.2009

В связи с некоторыми неурядицами Записки из тех. отдела какое-то время были в дауне. Я был приятно удивлен, что ко мне стучались люди и дальнейшей интересовались судьбой блога. В конечном итоге я нашел в себе силы и таки пднял блог из пепла.

Новый домен

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

Новый движок

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

Новый адрес фида

Меняя домен и название блога, я хочу также сменить адрес фида, чтобы совсем отойти от концепции HTML-блога, а заодно почистить счетчик количества подписчиков от мертвых душ. Новый адрес фида: http://feeds2.feedburner.com/railorz. Старый адрес очень скоро протухнет.

Я очень рад тому, что вы меня читаете, спасибо вам за это :)

Блог о софте для MacOS

Опубликовано 01.12.2008

Псс… Я тут совершенно случайно выяснил, что Константин Тумалевич помимо программирования на рельсах, еще и ведет блог о софте для Mac OS. Я сам не вхожу в число счастливых обладателей этой замечательной системы, однако уверен что среди вас, дорогие читатели, наверняка найдутся таковые :)

101 читатель

Опубликовано 17.04.2007

Вчера свершилось знаковое событие для моего маленького журнала и для меня лично. FeedBurner показывает за вчера 101 читателя моей RSS-ленты. Это значит, что вас, уважаемые читатели моего блога, уже больше сотни - и мне чертовски приятно осознавать этот факт :) Спасибо вам огромное за ваше внимание! Я постараюсь, чтобы мой журнал день ото дня становился все более интересным и полезным для вас.

Спам фильтр спасает

Опубликовано 16.04.2007

С момента переезда на Mephisto ощутил существенное облегчение в плане модерирования комментариев. Когда был Wordpress, приходилось удалять в среднем 100-120 спамерских комментов в сутки - и это при настроенных спам-фильтрах. Сейчас, используя Akismet, я получаю где-то 1-2 спам-комментария в неделю. Может быть, это связано с тем, что страницы блога еще не переиндексировались поисковыми системами и, соответственно, не попали под парсеры господ дорвейщиков. Однако, сам факт передышки несказанно радует.

Осталось теперь только найти (или написать) всякие разные плагины для подписки на комментарии, уведомлений по e-mail и прочих вкусностей, которых в Mephisto пока нету. Ну и починить кодировку архивов.