Ну ладно, тогда я сам попробую.
Я, конечно, был наивен, надеясь одним изменением в SQL-запросе всё разрулить. Итак, что пришлось проделать с
viewforum.php:
Для начала полезная ссылка для понимания принципов работы отметок о прочитанных сообщениях:
http://ilantipov.livejournal.com/2012.html
Так как при просмотре форума предполагается, что отображаемые темы из
topicrow относятся к этому форуму (спасибо КО, хотя еще есть shadow-топики и глобальные объявления), то все проверки на «прочитанность» топика делаются из контекста текущего
forum_id. Это и предстояло изменить.
Когда я размышлял над тем, как обойти это ограничение, я вспомнил про страницу с активными темами — там тоже собраны топики из разных форумов, но отметки о непрочитанных сообщениях работают корректно. Я немного покопался в
search.php и криворукими костылями перенес некоторые решения оттуда. Главная идея очевидна — передавать в функции
get_topic_tracking и
get_complete_topic_tracking (они отвечают за отметки о непрочитанных сообщениях) вместе со списком сообщений правильный
forum_id, который теперь не общий и неизменный для всех тем, а меняющийся в зависимости от топиков.
Итак, где-нибудь в начале файла устанавливаем триггер-включалку и объявляем список форумов, где подфорумы будут работать как метки.
Код: Выделить всё
define('SUBFORUMS_AS_TAGS', true);
$subforums_as_tags_forums_id = array(4, 6, 14);
В
$forum_data хранятся данные о форуме. Большинством свойств родительского форума (стиль, имя, тип форума, пароль и т.д.) пользуемся без изменений, эти свойства у подфорумов значение не имеют, поэтому этот массив переопределять не будем. Но одно свойство надо разузнать у подфорумов —
mark_time, оно отвечает за правильное отображение непрочитанных топиков. Сохраняем заготовку формирующего
$forum_data SQL-запроса на будущее:
SEARCH
Код: Выделить всё
$sql = "SELECT f.* $lastread_select
FROM $sql_from
WHERE f.forum_id = $forum_id";
ADD AFTER
Код: Выделить всё
$sql_for_subforums = "SELECT f.* $lastread_select
FROM $sql_from";
После того, как список подфорумов определен, пользуемся заготовленным куском запроса и сохраняем значения
mark_time форума и подфорумов.
SEARCH
Код: Выделить всё
// Dump out the page header and load viewforum template
page_header($forum_data['forum_name']);
ADD BEFORE
Код: Выделить всё
if(SUBFORUMS_AS_TAGS && in_array($forum_id, $subforums_as_tags_forums_id) && sizeof($active_forum_ary['forum_id']) ){
if ($user->data['is_registered'] && $config['load_db_lastread'])
{
$sql_for_subforums .= ' WHERE f.forum_id IN ('.$forum_id.', '.implode(", ", $active_forum_ary['forum_id']).')';
$result_subforums = $db->sql_query($sql_for_subforums);
while ($row = $db->sql_fetchrow($result_subforums)){
$forum_data_subforums[$row['forum_id']] = $row;
}
$db->sql_freeresult($result_subforums);
}
}
Заводим массив для распределения сведения о топиках по форумам.
SEARCH
Код: Выделить всё
$rowset = $announcement_list = $topic_list = $global_announce_list = array();
REPLACE WITH
Код: Выделить всё
$forums = $rowset = $announcement_list = $topic_list = $global_announce_list = array();
Вмешиваемся в первый SQL-запрос на список тем
SEARCH
Код: Выделить всё
if ($forum_data['forum_type'] == FORUM_POST)
{
// Obtain announcements ... removed sort ordering, sort by time in all cases
ADD AFTER
Код: Выделить всё
if(SUBFORUMS_AS_TAGS && in_array($forum_id, $subforums_as_tags_forums_id) && sizeof($active_forum_ary['forum_id']) ){
$subforo_list = $forum_id.', '.implode(", ", $active_forum_ary['forum_id']);
} else{
$subforo_list = $forum_id;
}
SEARCH
Код: Выделить всё
'WHERE' => 't.forum_id IN (' . $forum_id . ', 0)
REPLACE WITH
Код: Выделить всё
'WHERE' => 't.forum_id IN (' . $subforo_list . ', 0)
В цикле сбора результатов запроса раскладываем топики по форумам
SEARCH
ADD BEFORE
Код: Выделить всё
$forums[$row['forum_id']]['rowset'][$row['topic_id']] = &$rowset[$row['topic_id']];
То же самое с другими запросами
SEARCH
REPLACE WITH
Код: Выделить всё
//здесь айди не только отцовского форума, но и подфорумов
if(SUBFORUMS_AS_TAGS && in_array($forum_id, $subforums_as_tags_forums_id) && sizeof($active_forum_ary['forum_id']) ){
$sql_where = 't.forum_id IN (' . $forum_id.', '.implode(", ", $active_forum_ary['forum_id']).')';
} else{
$sql_where = 't.forum_id = ' . $forum_id;
}
SEARCH
REPLACE WITH
SEARCH
ADD AFTER
Код: Выделить всё
$forums[(int) $row['forum_id']]['topic_list'][] = (int) $row['topic_id'];
SEARCH
Код: Выделить всё
if ($row['topic_status'] == ITEM_MOVED)
{
$shadow_topic_list[$row['topic_moved_id']] = $row['topic_id'];
}
$rowset[$row['topic_id']] = $row;
ADD AFTER
Код: Выделить всё
$forums[$row['forum_id']]['rowset'][$row['topic_id']] = &$rowset[$row['topic_id']];
Ну и финальный блок, где всё разруливается. Проверяется триггер и в зависимости от его значения запускается новый или старый вариант обработки. В новом варианте всё просто — блок оборачивается в цикл по форумам, ну и аргументы при вызове функций подмениваются на сохраненные ранее в массивы
$forums и
$forum_data_subforums.
SEARCH
Код: Выделить всё
if ($config['load_db_lastread'] && $user->data['is_registered'])
{
$topic_tracking_info = get_topic_tracking($forum_id, $topic_list, $rowset, array($forum_id => $forum_data['mark_time']), $global_announce_list);
$mark_time_forum = (!empty($forum_data['mark_time'])) ? $forum_data['mark_time'] : $user->data['user_lastmark'];
}
else if ($config['load_anon_lastread'] || $user->data['is_registered'])
{
$topic_tracking_info = get_complete_topic_tracking($forum_id, $topic_list, $global_announce_list);
if (!$user->data['is_registered'])
{
$user->data['user_lastmark'] = (isset($tracking_topics['l'])) ? (int) (base_convert($tracking_topics['l'], 36, 10) + $config['board_startdate']) : 0;
}
$mark_time_forum = (isset($tracking_topics['f'][$forum_id])) ? (int) (base_convert($tracking_topics['f'][$forum_id], 36, 10) + $config['board_startdate']) : $user->data['user_lastmark'];
}
REPLACE WITH
Код: Выделить всё
if( SUBFORUMS_AS_TAGS && in_array($forum_id, $subforums_as_tags_forums_id) && sizeof($active_forum_ary['forum_id']) ){//новый вариант
foreach ($forums as $forum_id => $forum){
if ($config['load_db_lastread'] && $user->data['is_registered'])
{
$topic_tracking_info += get_topic_tracking($forum_id, $forum['topic_list'], $forum['rowset'], array($forum_id => $forum_data_subforums[$forum_id]['mark_time']), $global_announce_list);
$mark_time_forum = (!empty($forum_data_subforums[$forum_id]['mark_time'])) ? $forum_data_subforums[$forum_id]['mark_time'] : $user->data['user_lastmark'];
}
else if ($config['load_anon_lastread'] || $user->data['is_registered'])
{
$topic_tracking_info += get_complete_topic_tracking($forum_id, $forum['topic_list'], $global_announce_list);
if (!$user->data['is_registered'])
{
$user->data['user_lastmark'] = (isset($tracking_topics['l'])) ? (int) (base_convert($tracking_topics['l'], 36, 10) + $config['board_startdate']) : 0;
}
$mark_time_forum = (isset($tracking_topics['f'][$forum_id])) ? (int) (base_convert($tracking_topics['f'][$forum_id], 36, 10) + $config['board_startdate']) : $user->data['user_lastmark'];
}
}
unset($forums);
} else{//старый вариант
if ($config['load_db_lastread'] && $user->data['is_registered'])
{
$topic_tracking_info = get_topic_tracking($forum_id, $topic_list, $rowset, array($forum_id => $forum_data['mark_time']), $global_announce_list);
$mark_time_forum = (!empty($forum_data['mark_time'])) ? $forum_data['mark_time'] : $user->data['user_lastmark'];
}
else if ($config['load_anon_lastread'] || $user->data['is_registered'])
{
$topic_tracking_info = get_complete_topic_tracking($forum_id, $topic_list, $global_announce_list);
if (!$user->data['is_registered'])
{
$user->data['user_lastmark'] = (isset($tracking_topics['l'])) ? (int) (base_convert($tracking_topics['l'], 36, 10) + $config['board_startdate']) : 0;
}
$mark_time_forum = (isset($tracking_topics['f'][$forum_id])) ? (int) (base_convert($tracking_topics['f'][$forum_id], 36, 10) + $config['board_startdate']) : $user->data['user_lastmark'];
}
}
Работает на боевом форуме, пока баги не замечены. Хотя наверняка есть какие-то нестыковки в особых случаях, которые я не тестировал. Оптимизировать там тоже дофига чего, но я пока добивался только функциональности. Ну и, конечно, можно оформить цивилизованно как МОД, чтобы включалось-выключалось для форумов в админке.