Joomla и Last-modified

    В тот момент, когда 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; добавляем следующий код:

    1. $last_modified=strtotime($this->item->modified);
    2. JResponse::setHeader('Last-Modified',gmdate('D, d M Y H:i:s', $last_modified).' GMT');
    3. if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])>=$last_modified){
    4. 	header($_SERVER['SERVER_PROTOCOL'].' 304 Not Modified');
    5. 	die;
    6. 	}

    А в html/com_content/category/blog.php добавляем:

    1. $last_modified=strtotime($this->category->modified_time);
    2. foreach($this->items as $post){
    3. 	$lm=strtotime($post->modified);
    4. 	if($last_modified<$lm)
    5. 		$last_modified=$lm;
    6. 	}
    7. JResponse::setHeader('Last-Modified',gmdate('D, d M Y H:i:s', $last_modified).' GMT');
    8. if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])>=$last_modified){
    9. 	header($_SERVER['SERVER_PROTOCOL'].' 304 Not Modified');
    10. 	die;
    11. 	}

    Шаг 3 (Для Joomla 1.5).

    В данной версии Джумлы слегка изменена объектная модель и есть некоторые «Палки в колёса» от разработчиков. Если вы проверите заголовки, то обнаружите что Last-modified равен текущему времени (с учётом временных поясов). Глубоко в дебрях CMS (/libraries/joomla/environment/response.php) спрятан код, который и отдаёт такие странные заголовки. Придётся его закомментировать:

    1. if (JResponse::allowCache() === false){
    2. 	//JResponse::setHeader( 'Expires', 'Mon, 1 Jan 2001 00:00:00 GMT', true );
    3. 	//JResponse::setHeader( 'Last-Modified', gmdate("D, d M Y H:i:s") . ' GMT', true );
    4. 	//JResponse::setHeader( 'Cache-Control', 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0', false);
    5. 	//JResponse::setHeader( 'Pragma', 'no-cache' ); 											// HTTP 1.0
    6. 	}

    Далее — всё как в предыдущем варианте, с учётом того что для статьи вместо $this->item надо использовать $this->article, а дата изменения категории не сохраняется (только дата каждого поста).

    В html/com_content/article/default.php после defined('_JEXEC') or die; добавляем следующий код:

    1. $last_modified=strtotime($this->article->modified);
    2. JResponse::setHeader('Last-Modified',gmdate('D, d M Y H:i:s', $last_modified).' GMT');
    3. if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])>=$last_modified){
    4. 	header($_SERVER['SERVER_PROTOCOL'].' 304 Not Modified');
    5. 	die;
    6. 	}

    А в html/com_content/category/blog.php добавляем:

    1. $last_modified=NULL;
    2. foreach($this->items as $post){
    3. 	$lm=strtotime($post->modified);
    4. 	if((empty($last_modified))||($last_modified<$lm))
    5. 		$last_modified=$lm;
    6. 	}
    7. JResponse::setHeader('Last-Modified',gmdate('D, d M Y H:i:s', $last_modified).' GMT');
    8. if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])>=$last_modified){
    9. 	header($_SERVER['SERVER_PROTOCOL'].' 304 Not Modified');
    10. 	die;
    11. 	}

    Вместо заключения

    Всё, готово! Проверяем заголовки и наслаждаемся результатом. Хотя данный вариант подходит всего лишь для статей и для блогов, менять остальные компоненты можно аналогичным образом.

    Итак, мы научились менять вывод компонентов и успешно внедрили вывод заголовка Last-modified для статей и блогов, с чем я вас и поздравляю!

    Вопросы пишите в комментариях.

    Комментарии

    05.05.2013 01:39:16
    Avatar of РоманРоман
    Не работает на Joomla 3
    26.10.2013 06:05:04
    Avatar of MilaMila
    Да и с компонентом zoo врядли получится :(
    30.10.2013 11:53:06
    Avatar of КонсервКонсерв
    @Роман
    На Joomla 3 не проверял. Думаю, там не сильно отличается. С прямыми руками поправить - как два пальца.

    @Mila
    Естественно. Для каждого компонента надо править отдельно. Плюс, модули пролетают. Разве что ковырять саму джумлу и каждый модуль/компонент в отдельности...
    К сожалению, разработчики Joomla многих вещей не предусмотрели. Однако, данный костыль - наиболее элегантный из всех, которые я видел.
    09.12.2013 03:07:01
    Avatar of ЯрикЯрик
    А для Joomshopping, для товаров и категорий можно изменить ???
    11.12.2013 10:21:44
    Avatar of КонсервКонсерв
    @Ярик
    Joomshopping не смотрел, но, думаю, можно.

    Без проблем патчил VirtueMart по алгоритму, описанному в статье.
    24.12.2013 10:55:29
    Avatar of Юра ГалинЮра Галин
    Рекомендую онлайн сервис для проверки Last Modified: http://last-modified.com/ru/if-modified-since.html
    24.12.2013 11:14:01
    Avatar of Юра ГалинЮра Галин
    @Роман
    Рекомендую для Joomla 3 плагин, у меня нормально запработало. Вот ссылка: http://www.sdf-group.ru/joomla/lmpluginj3.html
    15.01.2014 01:55:05
    Avatar of ВладимирВладимир
    У меня сайт на Joomla 3.2 и ни описанный способ, ни плагин не работают.
    Удалось на Денвере нахимичить следующее:
    В файле 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 = дата изменения материала. К сожалению, по непонятным мне причинам, этот код не работает на основном сервере.
    27.02.2014 12:14:22
    Avatar of СергейСергей
    да и на Joomla 2.5.7 не работает
    16.08.2014 11:33:30
    Avatar of 202202
    Спасибо за статью
    29.07.2015 09:03:56
    Avatar of AlexandrAlexandr
    Пилю https://BiOS72.ru.
    joomla 3
    Сделал так, в шаблоне вывода контента
    JResponse::allowCache(true);
    Готово!
    29.07.2015 10:14:32
    Avatar of AlexandrAlexandr
    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;
    }
    15.12.2015 06:12:28
    Avatar of КонсервКонсерв
    @Alexandr
    Спасибо за подсказку!
    05.05.2016 09:49:41
    Avatar of СергейСергей
    Если кому интересно, то вот по аналогии для компонента K2 - проверено работает (jommla 3.4.8)!

    /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;
    }
    06.05.2016 11:55:25
    Avatar of СергейСергей
    Кстати, в категории все-таки дата глючит: когда норм показывает, когда нет. Поэтому, я переделал вот так:

    вместо изменений в файле /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 на более универсальное, но у меня отлично и так работает.
    06.05.2016 04:16:30
    Avatar of КонсервКонсерв
    @Сергей
    Я думаю, это может не работать со специфическими сортировками, а так ок.

    Я бы ещё добавил проверку на дату изменения самой категории (например, если изменилось описание или МЕТА данные категории).

    Ещё вопрос - $this->primary это что? Массив? Объект?
    В одном месте вы работаете как с объектом ($this->primary->modified).
    А чуть ниже - как с массивом (foreach($this->primary as $post){)
    16.05.2016 11:57:47
    Avatar of СергейСергей
    Консерв, честно говоря, я не заморачивался объект это или массив, т.к. в php мало разбираюсь. А подставлял я больше методом тыка :) Для меня главное - чтоб работало.

    И еще заметил глюк, по крайней мере у меня: время показывается с вычитанием не 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;
    }
    07.06.2016 03:13:10
    Avatar of КонсервКонсерв
    @Сергей, тогда уж могут быть проблемы с летним/зимним временем :-)
    15.04.2017 07:55:57
    Avatar of ВикторВиктор
    Огромное спасибо! Не поленюсь написать!

    Решений этой проблемы нашел в интернете (даже в англоязычном) 3-4, ничего не работало, только этот способ сработал!

    Версия джумлы последняя - 3.6.5!
    16.04.2017 12:47:47
    Avatar of ВикторВиктор
    Нет! Рано радовался! Коды отдает верно, но вот не показывает корректно дату создания статьи, всегда показывает дату созданию текущего дня :((((((
    16.04.2017 12:57:20
    Avatar of ВикторВиктор
    Вставил то что писал Сергей - "для компонента K2 - проверено работает (jommla 3.4.8)!"
    Заработало! Ура!
    18.04.2017 11:40:03
    Avatar of КонсервКонсерв
    @Виктор, спасибо за отзыв!
    17.05.2019 02:21:58
    Avatar of gooseABUBSSEAFgooseABUBSSEAF
    Hello. And Bye.

    Adding comments is temporarily disabled for unregistered users.

    Go Top