В тот момент, когда HTTP-клиент (ваш браузер или поисковой робот) отправляет на сервер запрос на получение документа, сервер перед документом отправляет определённые заголовки. Это и Content-Type, без которого браузер не знает, что ему делать с документом и установка Cookie, без которых не будет работать авторизация на большинстве сайтов и многие другие заголовки.
Среди всех заголовков не последнюю роль играет Last-modified. Он не является обязательным, но этот заголовок очень важен. Во-первых, он уменьшает нагрузку на сервер и скорость повторной загрузки страницы (клиенты не запрашивают весь документ, зная что документ не изменялся). Во-вторых, он даёт поисковым роботам очень много информации. Без данного заголовка скорость индексации сайта падает в разы.
Я был несколько удивлён, если не сказать ошеломлён, узнав что Joomla не отдаёт заголовка Last-modified. Порывшись в рунете, я нашел несколько костылей, которые исправляют данный недочёт. Но они были настолько кривые, что мне даже страшно было предположить, что курили их автора.
Итак, небольшое знание внутренностей Joomla даёт решение нашей задачи. Выводом основного контента страницы занимаются компоненты (дополнительного - модули). Дефолтный компонент, который выводит статьи, блоги статей и т.п. называется com_content. За вывод контента компонента в Joomla отвечают так называемые views. В компоненте их может быть несколько (см. /components/com_content/views/). Можно править файлы прямо там, но при обновлении Joomla они слетят, так что лучше создать папку html в папке шаблона, где создать определённую структуру.
Практическая часть
Допустим, нам надо выводить корректный Last-Modified для статей и блога статей остальные варианты я пока не рассматриваю.
Шаг 1.
Создаём директорию (папку) /templates/<Название шаблона>/html/, в ней — директорию com_content и уже в com_content создаём две директории article и category. Они будут отвечать за вывод отдельной статьи и списка статей (блогов и т.п.).
Шаг 2.
Копируем в эти директории содержимое /components/com_content/views/article/ и /components/com_content/views/category/ соответственно. Теперь мы можем менять вывод статей и блогов, не боясь апдейта Джумлы.
Вы теперь можете менять содержимое html/com_content/article/default.php для изменения разметки вывода статьи и html/com_content/category/blog.php для того чтобы поменять вывод блога. Заметьте, изменения происходят в шаблоне, не трогая внутренности Joomla!
Шаг 3 (Для Joomla 2.5).
Но ведь задача была в том, чтобы добавить вывод заголовка Last-Modified! Сейчас мы этим и займёмся. В html/com_content/article/default.php после defined('_JEXEC') or die; добавляем следующий код:
$last_modified=strtotime($this->item->modified);
JResponse::setHeader('Last-Modified',gmdate('D, d M Y H:i:s', $last_modified).' GMT');
if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])>=$last_modified){
header($_SERVER['SERVER_PROTOCOL'].' 304 Not Modified');
die;
}
А в html/com_content/category/blog.php добавляем:
$last_modified=strtotime($this->category->modified_time);
foreach($this->items as $post){
$lm=strtotime($post->modified);
if($last_modified<$lm)
$last_modified=$lm;
}
JResponse::setHeader('Last-Modified',gmdate('D, d M Y H:i:s', $last_modified).' GMT');
if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])>=$last_modified){
header($_SERVER['SERVER_PROTOCOL'].' 304 Not Modified');
die;
}
Шаг 3 (Для Joomla 1.5).
В данной версии Джумлы слегка изменена объектная модель и есть некоторые «Палки в колёса» от разработчиков. Если вы проверите заголовки, то обнаружите что Last-modified равен текущему времени (с учётом временных поясов). Глубоко в дебрях CMS (/libraries/joomla/environment/response.php) спрятан код, который и отдаёт такие странные заголовки. Придётся его закомментировать:
if (JResponse::allowCache() === false){
//JResponse::setHeader( 'Expires', 'Mon, 1 Jan 2001 00:00:00 GMT', true );
//JResponse::setHeader( 'Last-Modified', gmdate("D, d M Y H:i:s") . ' GMT', true );
//JResponse::setHeader( 'Cache-Control', 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0', false);
//JResponse::setHeader( 'Pragma', 'no-cache' ); // HTTP 1.0
}
Далее — всё как в предыдущем варианте, с учётом того что для статьи вместо $this->item надо использовать $this->article, а дата изменения категории не сохраняется (только дата каждого поста).
В html/com_content/article/default.php после defined('_JEXEC') or die; добавляем следующий код:
$last_modified=strtotime($this->article->modified);
JResponse::setHeader('Last-Modified',gmdate('D, d M Y H:i:s', $last_modified).' GMT');
if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])>=$last_modified){
header($_SERVER['SERVER_PROTOCOL'].' 304 Not Modified');
die;
}
А в html/com_content/category/blog.php добавляем:
$last_modified=NULL;
foreach($this->items as $post){
$lm=strtotime($post->modified);
if((empty($last_modified))||($last_modified<$lm))
$last_modified=$lm;
}
JResponse::setHeader('Last-Modified',gmdate('D, d M Y H:i:s', $last_modified).' GMT');
if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])>=$last_modified){
header($_SERVER['SERVER_PROTOCOL'].' 304 Not Modified');
die;
}
Вместо заключения
Всё, готово! Проверяем заголовки и наслаждаемся результатом. Хотя данный вариант подходит всего лишь для статей и для блогов, менять остальные компоненты можно аналогичным образом.
Итак, мы научились менять вывод компонентов и успешно внедрили вывод заголовка Last-modified для статей и блогов, с чем я вас и поздравляю!
Вопросы пишите в комментариях.
Комментарии
На Joomla 3 не проверял. Думаю, там не сильно отличается. С прямыми руками поправить - как два пальца.
@Mila
Естественно. Для каждого компонента надо править отдельно. Плюс, модули пролетают. Разве что ковырять саму джумлу и каждый модуль/компонент в отдельности...
К сожалению, разработчики Joomla многих вещей не предусмотрели. Однако, данный костыль - наиболее элегантный из всех, которые я видел.
Joomshopping не смотрел, но, думаю, можно.
Без проблем патчил VirtueMart по алгоритму, описанному в статье.
Рекомендую для Joomla 3 плагин, у меня нормально запработало. Вот ссылка: http://www.sdf-group.ru/joomla/lmpluginj3.html
Удалось на Денвере нахимичить следующее:
В файле mydomen.ru/libraries/joomla/application/web.php 429ю строку
$this->setHeader('Last-Modified', gmdate('D, d M Y H:i:s') . ' GMT', true);
на это:
$this->setHeader('Last-Modified', $this->modifiedDate->format('D, d M Y H:i:s'));
В итоге тестовый сайт отдает Last Modified = дата изменения материала. К сожалению, по непонятным мне причинам, этот код не работает на основном сервере.
joomla 3
Сделал так, в шаблоне вывода контента
JResponse::allowCache(true);
Готово!
$last_modified=strtotime($this->item->modified);
JResponse::setHeader('Last-Modified',gmdate('D, d M Y H:i:s', $last_modified).' GMT',true);
JResponse::setHeader('Cache-Control','public',true);
if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])>=$last_modified){
header($_SERVER['SERVER_PROTOCOL'].' 304 Not Modified');
die;
}
Спасибо за подсказку!
/templates/[ВАШ_ШАБЛОН]/html/com_k2/templates/default/category_item.php
После:
defined('_JEXEC') or die;
Добавить:
$last_modified=strtotime($this->item->modified);
foreach($this->item as $post){
$lm=strtotime($post->modified);
if($last_modified<$lm)
$last_modified=$lm;
}
JResponse::allowCache(true);
JResponse::setHeader('Last-Modified',gmdate('D, d M Y H:i:s', $last_modified).' GMT',true);
JResponse::setHeader('Cache-Control','public',true);
if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])>=$last_modified){
header($_SERVER['SERVER_PROTOCOL'].' 304 Not Modified');
die;
}
/templates/[ВАШ_ШАБЛОН]/html/com_k2/templates/default/item.php
После:
defined('_JEXEC') or die;
Добавить:
JResponse::allowCache(true);
$last_modified=strtotime($this->item->modified);
JResponse::setHeader('Last-Modified',gmdate('D, d M Y H:i:s', $last_modified).' GMT',true);
JResponse::setHeader('Cache-Control','public',true);
if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])>=$last_modified){
header($_SERVER['SERVER_PROTOCOL'].' 304 Not Modified');
die;
}
вместо изменений в файле /templates/[ВАШ_ШАБЛОН]/html/com_k2/templates/default/category_item.php
я сделал в /templates/[ВАШ_ШАБЛОН]/html/com_k2/templates/default/category.php
После:
defined('_JEXEC') or die;
Добавить:
defined('_JEXEC') or die;
$last_modified=strtotime($this->primary->modified);
foreach($this->primary as $post){
$lm=strtotime($post->modified);
if($last_modified<$lm)
$last_modified=$lm;
}
JResponse::allowCache(true);
JResponse::setHeader('Last-Modified',gmdate('D, d M Y H:i:s', $last_modified).' GMT',true);
JResponse::setHeader('Cache-Control','public',true);
if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])>=$last_modified){
header($_SERVER['SERVER_PROTOCOL'].' 304 Not Modified');
die;
}
* $this->primary - это у меня все материалы в категории в виде первичных материалов, других нет.
Но нужно учесть, что также есть основные ($this->leading), вторичные ($this->secondary), ссылки ($this->links)
Пусть меня поправит тот кто лучше разбирается в php чем можно заменить $this->primary на более универсальное, но у меня отлично и так работает.
Я думаю, это может не работать со специфическими сортировками, а так ок.
Я бы ещё добавил проверку на дату изменения самой категории (например, если изменилось описание или МЕТА данные категории).
Ещё вопрос - $this->primary это что? Массив? Объект?
В одном месте вы работаете как с объектом ($this->primary->modified).
А чуть ниже - как с массивом (foreach($this->primary as $post){)
И еще заметил глюк, по крайней мере у меня: время показывается с вычитанием не 3-х часов (поправка на UTC +3 часа), а 6-ти (!!!) часов. Поэтому, прибавил 3 часа в секундах 10800 и получилось так:
defined('_JEXEC') or die;
$last_modified=strtotime($this->primary->modified)+10800;
foreach($this->primary as $post){
$lm=strtotime($post->modified);
if($last_modified<$lm)
$last_modified=$lm+10800;
}
JResponse::allowCache(true);
JResponse::setHeader('Last-Modified',gmdate('D, d M Y H:i:s', $last_modified).' GMT',true);
JResponse::setHeader('Cache-Control','public',true);
if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])>=$last_modified){
header($_SERVER['SERVER_PROTOCOL'].' 304 Not Modified');
die;
}
Решений этой проблемы нашел в интернете (даже в англоязычном) 3-4, ничего не работало, только этот способ сработал!
Версия джумлы последняя - 3.6.5!
Заработало! Ура!
Adding comments is temporarily disabled for unregistered users.