Pull to refresh

Улучшаем форумный движок на стороне клиента

Reading time7 min
Views1.1K
Многие из нас знают о возможности менять внешний вид веб-сайтов локальными средствами, без модификации файлов на сервере. Существуют разные способы делать это, самые популярные — пользовательские скрипты и стили, которые автоматически применяются браузером к загруженной странице. Разумеется, возможности такой правки весьма ограничены, и решить какую-то серьёзную задачу, требующую нестандартного запроса к базе, таким способом не получится.

Тем не менее, даже в этом случае может оказаться, что не всё так безнадёжно. В данной статье я хочу рассказать о собственном опыте создания локальной надстройки для форумного движка phpBB2, которая исправляет отображение статуса прочитанности для тем и сообщений. Хоть результатом данной попытки оказался вполне работоспособный продукт, которым я теперь постоянно пользуюсь, целью написания данной статьи всё же является не презентация продукта, а описание подхода к решению проблемы. Код полученной программы я выкладываю, но в силу специфики задачи он недостаточно универсален и не может использоваться как есть, без предварительной (и довольно кропотливой) настройки под движок конкретного сайта. Решил предупредить об этом заранее, чтобы не было разочарований.

Ну а теперь более подробно о постановке задачи.

Несмотря на то, что уже давно актуальной версией phpBB считается третья, вторая всё ещё довольно часто встречается в Интернете. Вот список недостатков phpBB2, с которыми призван бороться мой инструмент:
  1. После перелогина или перезапуска браузера, а также по истечении некоторого промежутка времени неактивности все непрочитанные темы автоматически помечаются прочитанными.
  2. При открытии темы она становится прочитанной вся целиком, какую бы страницу вы ни открыли.
  3. Статус прочитанности хранится в куках в виде сериализованного массива. Максимальный размер куков обычно ограничен 4 Кб, что позволяет хранить статус только для ~140 тем. Для активного форума это очень немного.
Некоторые, возможно, пожмут плечами: и в чём, собственно, проблема? А проблема в том, что если вы стремитесь быть в курсе всех дел, то пометка непрочтённых тем и сообщений оранжевым листочком (в базовом скине) очень здорово помогает быстро видеть, какие темы обновились, а какие — нет. Если вы, скажем, решили сейчас зайти на форум и быстренько ответить на какое-нибудь сообщение, а чтение остальных обновившихся тем отложить на потом, при следующем посещении статус непрочитанности окажется сброшенным, и придётся искать новые сообщения вручную, вспоминая, когда было последнее посещение каждой конкретной темы, и выискивая по датам или по текстам постов, что же в ней появилось с того момента. Да и даже если вы всегда читаете все темы сразу, это не является гарантией: ведь браузеры иногда могут падать, компьютер может сбоить, а свет может отключаться (и ИБП есть не у всех).

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

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

В качестве формата базы я выбрал текстовый файл с набором строк вида «00000000000000». Каждая строка соответствует одной теме, номер строки (считая от нуля) — идентификатор темы, а в качестве содержимого строки записывается временна́я метка последнего посещения в формате UNIX-time. Нулевой темы не существует, поэтому нулевая строка используется особым образом: она хранит дату/время для всего форума в целом, чтобы можно было быстро пометить прочитанными сразу все темы форума. Таким образом, для каждой темы реальным временем последнего посещения считается максимальное из двух значений: общефорумного и конкретно этой темы.

Почему я остановился именно на текстовом формате? Во-первых, чтобы удобно было при необходимости исправлять ошибки, не влезая в двоичный редактор. Во-вторых, прокси-сервер у меня написан на Перле, а в нём работать с текстовыми файлами удобнее, чем с двоичными. Парсинг числа из текстовой строки — не такая уж ресурсоёмкая операция, а благодаря фиксированной длине строки переходить к нужной записи можно напрямую по индексу, не считывая весь файл построчно.

Что касается реализации прокси-сервера, язык Перл был выбран по той причине, что на нём очень удобно работать с текстом (а разбор HTML будет, естественно, ключевой частью). Результирующая скорость по моим меркам оказалась вполне приемлемой (во всяком случае, потенциальный прирост производительности от перехода на другой язык для меня выглядит менее существенным, чем затраты сил и времени). Прокси-сервер слушает свой порт и, получив запрос от браузера, посылает запрос целевому серверу, считывает ответ и отправляет содержимое полученной страницы браузеру. На своём пути страница проходит через фильтр, поведение которого зависит от целевого адреса. Если это один из нужных нам форумов, причём не что угодно, а один из скриптов viewtopic.php, viewforum.php, index.php (или просто корневой URL форума), то фильтр начинает парсить страницу, подменяя ярлычки тем и сообщений на основе даты/времени последнего посещения, взятой из локальной базы. В противном случае фильтр не работает, а просто отправляет немодифицированное содержимое браузеру.

Самая сложная и непредсказуемая часть — это парсинг. Проблема в том, что на каждом конкретном форуме могут быть установлены разные темы и расширения, так что получаемый с сервера HTML-код страниц варьируется в очень широких пределах. Чтобы учесть особенности разных форумов, я захардкодил лишь ключевые структурные особенности движка phpBB2, а конкретные сигнальные строки вынес в отдельные модули, которые подгружаются прокси-сервером при старте и позволяют обрабатывать каждый форум по своим наборам правил. Разумеется, если потребуется добавить поддержку нового форума, всю работу по выделению и настройке сигнальных строк придётся проводить вручную, путём анализа полученной с сервера HTML-страницы. Если изменения в движке небольшие, всё только этим и ограничится. Но может оказаться и так, что движок был серьёзно переработан, и HTML-структура стала совершенно иной. Тогда весь основной модуль придётся переделывать, и не факт, что вообще удастся сохранить «мультифорумность». Не исключено, что проще будет держать отдельный вариант прокси-сервера, заточенный специально под один из «навороченных» форумов.

Для корректной работы парсинга также придётся внести изменения в свой форумный профиль. Дело в том, что по умолчанию движок выдаёт дату/время сообщений без указания секунд, а поскольку время брать нам больше неоткуда, окажется, что погрешность при расставлении пометок составит одну минуту, что, конечно, слишком много. Поэтому необходимо в своих профилmys[ настройках выбрать более полный формат времени, с секундами. Я остановился на формате «D d.m.Y, G:i:s», что позволит выводить дату в виде «Mon 14.02.2011, 10:57:44» (конечно, день недели прокси-серверу не нужен, но удобства для себя любимого тоже забывать не стоит). Если вам удобнее другой формат, надо будет внести соответствующие исправления в функцию разбора временно́й метки. Нельзя забывать и про модификации phpBB, которые вместо даты могут вставлять слова «сегодня» или «вчера». К сожалению, это всё тоже потребует доработки кода.

Ну и осталось упомянуть ещё несколько особенностей.
  • Пожалуй, главная проблема — это то, что крайне неудобно использовать такой прокси-сервер на постоянной основе. Если тормозную работу при работе с одним-единственным форумом можно пережить, то терпеть тормоза в повседневной работе будет очень неприятно. Я для себя решил эту проблему следующим образом: у меня давно уже используется Proxomitron в качестве резалки рекламы и добавлялки всяких плюшек. В числе прочего у него есть возможность использовать разные прокси-серверы в зависимости от запрашиваемого адреса. Я просто создал в нём правила, чтобы нужные мне форумы грузились через мой прокси (причём не целиком, а только обрабатываемые страницы), а всё остальное работало бы напрямую, поэтому мне данный недостаток неудобств не доставляет. Те, кто не пользуется Проксомитроном, могут поискать какие-нибудь альтернативные варианты или просто настроить себе в браузере возможность быстрого ручного переключения проксей (если, конечно, в браузере есть такая возможность).
  • Мне не хотелось реализовывать полноценный HTTP-сервер или искать готовую реализацию, поэтому я ограничился простенькой самописной, но довольно ограниченной версией (поддерживается только HTTP/1.0 с принудительным отключением gzip-сжатия). Лично мне этого хватает, но кому-то этих возможностей может показаться мало.
  • В phpBB есть ещё одна проблема, которая меня успела подзадолбать. Если открыть или обновить несколько форумных страниц одновременно, движок выбрасывает меня с форума (что в сочетании со сбросом статусов прочитанности тем просто приводило меня в бешенство). Я пока не разобрался в причинах такого поведения, но мой прокси-сервер заодно дал возможность обойти и эту проблему. Изначально он был многопоточным, чтобы обработка одной страницы не тормозила обработку других страниц. Я закомментировал код, отвечающий за многопоточность, и получил более медленную, но зато последовательную обработку страниц, так что теперь я могу открыть сразу кучу ссылок в фоновых вкладках, почти не беспокоясь, что меня вылогинит (конечно, теперь это не так фатально, но лишний раз вводить логин/пароль и обновлять кучу уже открытых страниц тоже как-то не хочется). Недостатки такого решения, конечно, тоже есть: если одна из страниц почему-то подвиснет, остальные не будут грузиться, и придётся либо ждать вылета по таймауту, либо перезапускать прокси-сервер. Да и при работе с несколькими форумами ждать одного из-за загрузки другого неправильно. Решением может быть запуск нескольких независимых потоков, по одному на каждый форум, или запуск нескольких экземпляров прокси-серверов на разных портах. На всякий случай код многопоточности я не удалил, а просто закомментировал, так что по желанию его можно раскомментировать и пользоваться нормальной многопоточной версией.
  • Прокси-сервер не умеет следить за количеством строк в базе и добавлять новые по мере необходимости (скажу честно: просто лень было реализовывать), поэтому надо ему предварительно создать файл, заполненный нулевыми строками. Количество строк зависит от форума (точнее, от количества тем в нём). Во избежание проблем лучше создать файл сразу с хорошим запасом.
  • В прокси-сервере присутствует также поддержка для phpBB-мода «Просмотр отдельного сообщения» (скрипт viewpost.php). При просмотре отдельного поста время посещения темы в базе не обновляется, т.к. в этом случае легко упустить непрочитанные сообщения, предшествующие просматриваемому. Если кто-то считает это неправильным, код легко подправить, убрав один if.

Ну и если кого-то вся эта морока ещё не отпугнула, сам серверок можно скачать отсюда (архив, 5 Кб). В комплекте идёт набор правил для официального форума Total Commander, который я и взял за основу в своих экспериментах и ради которого, в основном, и затевалась вся эта бодяга.
Tags:
Hubs:
+2
Comments9

Articles