Вывод новостей сразу в нескольких новостных лентах — различия между версиями
Mad grant (обсуждение | вклад) (Новая страница: «category:Написание кастомных макросовКатегория: Верстка в XSLTКатегория:Верстка в TPL[[Кат…») |
Mad grant (обсуждение | вклад) |
||
(не показано 26 промежуточных версий этого же участника) | |||
Строка 1: | Строка 1: | ||
− | [[category:Написание кастомных макросов | + | [[category:Написание кастомных макросов]][[Категория:API]][[Категория:Модуль_Новости]] |
'''Актуально для версии 2.9.1''' | '''Актуально для версии 2.9.1''' | ||
+ | |||
+ | == Задача == | ||
+ | |||
+ | Иногда необходимо, чтобы одна и та же новость отображалась в нескольких лентах новостей, стандартными средствами можно создавать виртуальные или просто копии | ||
+ | новости и перетаскивать их через модуль "Структура" по нужным разделам, а если на сайте сложная структура новостных лент, это не совсем удобно. | ||
+ | |||
+ | Поэтому, нам нужно реализовать функционал, который позволит указывать на странице конкретной новости, в каком разделе её выводить. | ||
+ | |||
+ | == Решение == | ||
+ | |||
+ | Со стороны административной панели, нам понадобиться создать в типе данных 'Новость' поле типа "ссылка на дерево" с идентификатором 'rubric_link', все | ||
+ | эти действия производятся в модуле "Шаблоны данных", в результате мы получим примерно следующее: | ||
+ | |||
+ | [[Файл:News_data_type.png]] | ||
+ | |||
+ | Теперь мы можем у каждой новости указать раздел для вывода: | ||
+ | |||
+ | [[Файл:Tree.png]] | ||
+ | |||
+ | Разделы мы указали, теперь нам понадобиться кастомный макрос, который будет выводить новости из конкретной ленты новостей, плюс новости из всех лент, у которых в поле с | ||
+ | идентификатором 'rubric_link' выбрана эта лента новостей. Код данного макроса и его описание даны ниже. | ||
+ | |||
+ | == Описание макроса == | ||
+ | |||
+ | %news customNewsList()% — выводит новости из выбранной ленты и новости из всех лент новостей, у которых в поле типа "ссылка на дерево" с идентификатором 'rubric_link' | ||
+ | указана выбранная лента. | ||
+ | |||
+ | '''Параметры: news customNewsList($elementPath,[$template = "default", $per_page = false, $ignore_paging = false])''' | ||
+ | |||
+ | '''$elementPath''' | ||
+ | Принимает id или путь до ленты новостей, новости из которой будут выводиться макроса | ||
+ | '''$template ''' | ||
+ | Принимает имя шаблона, по которому выводится результат макроса. В XSLT-шаблонизаторе игнорируется. | ||
+ | '''$per_page ''' | ||
+ | Принимает число, которое обозначает максимальное количество новостей. Если этот параметр не указывать, будет взято значение, указанное в настройках модуля «Новости». | ||
+ | '''$ignore_paging ''' | ||
+ | Принимаем булево значение («1» или «0»), указывающее макросу игнорировать значение текущей страницы списка вывода (параметр http-запроса p). То есть, если указать «1», | ||
+ | макрос будет всегда выводить только первую страницу списка новостей | ||
+ | |||
+ | Для применения этого макроса скопируйте содержимое листинга макроса в файл /classes/modules/news/__custom.php, и не забудьте указать имя метода в permissions.custom.php. | ||
+ | |||
+ | == Листинг макроса == | ||
+ | |||
+ | <source lang="php"> | ||
+ | public function customNewsList($elementPath, $template = "default", $per_page = false, $ignore_paging = false){ | ||
+ | //если $per_page не задан, то берем его из настроек модуля Новости | ||
+ | if(!$per_page){ | ||
+ | (int) $per_page = $this->per_page; | ||
+ | } | ||
+ | //загружаем шаблон | ||
+ | list($template_block, $template_block_empty, $template_line) = def_module::loadTemplates("news/".$template, "items_block", "items_block_empty", "item_block"); | ||
+ | //берем параметр 'p' | ||
+ | $currPageNum = (int) getRequest('p'); | ||
+ | //если в $elementPath указан путь до ленты, то вытаскиваем её id | ||
+ | $parentId = $this->analyzeRequiredPath($elementPath); | ||
+ | //если такой страницы нет, то кидаем исключение | ||
+ | if($parentId === false){ | ||
+ | throw new publicException(getLabel('error-page-does-not-exist', null, $elementPath)); | ||
+ | } | ||
+ | //задаем массив $itemsFromCurrLent, куда попадут новости из выбранной ленты | ||
+ | $itemsFromCurrLent = array(); | ||
+ | //получаем эти новости: | ||
+ | $currLentItems = new selector('pages'); | ||
+ | $currLentItems->types('object-type')->name('news', 'item'); | ||
+ | $currLentItems->where('hierarchy')->page($parentId); | ||
+ | //записываем их в массив $itemsFromCurrLent | ||
+ | $itemsFromCurrLent = $currLentItems->result(); | ||
+ | unset($currLentItems); | ||
+ | //задаем массив $itemsWithCurrLink, куда попадут новости из всех лент, у которых задана выбранная лента | ||
+ | $itemsWithCurrLink = array(); | ||
+ | //получаем эти новости | ||
+ | $allLentsItems = new selector('pages'); | ||
+ | $allLentsItems->types('object-type')->name('news', 'item'); | ||
+ | $allLentsItems->where('rubric_link')->equals($parentId); | ||
+ | //записываем их в массив $itemsWithCurrLink | ||
+ | $itemsWithCurrLink = $allLentsItems->result(); | ||
+ | unset($allLentsItems); | ||
+ | //задаем результирующий массив новостей | ||
+ | $resultItems = array(); | ||
+ | //помещаем в него все элементы из массива $itemsFromCurrLent и $itemsWithCurrLink, удаляем дубли. | ||
+ | $resultItems = array_unique(array_merge($itemsFromCurrLent, $itemsWithCurrLink)); | ||
+ | unset($itemsFromCurrLent, $itemsWithCurrLink); | ||
+ | //задаем числовую переменную $total, содержащую количество элементов массива $resultItems | ||
+ | (int) $total = count($resultItems); | ||
+ | //если массив не пустой, то | ||
+ | if($total>0){ | ||
+ | //сортируем элементы массива $resultItems по полю publish_time, то есть по дате публикации | ||
+ | usort($resultItems, function($a, $b){ | ||
+ | $a_publishTime = $a->getValue('publish_time'); | ||
+ | $b_publishTime = $b->getValue('publish_time'); | ||
+ | |||
+ | if ($a_publishTime == $b_publishTime) { | ||
+ | return 0; | ||
+ | } | ||
+ | return ($a_publishTime > $b_publishTime) ? -1 : 1; | ||
+ | }); | ||
+ | //задаем массив $limitedItems | ||
+ | $limitedItems = array(); | ||
+ | //если $ignore_paging = true, то массив $limitedItems становится результирующим | ||
+ | if($ignore_paging){ | ||
+ | $limitedItems = $resultItems; | ||
+ | //если $ignore_paging = false, то оставляем в результирующем массиве элементы, учитывая параметр 'p' и $per_page | ||
+ | }else{ | ||
+ | $offset = $currPageNum * $per_page; | ||
+ | $limitedItems = array_slice($resultItems, $offset, $per_page); | ||
+ | } | ||
+ | unset($resultItems); | ||
+ | //перебираем элементы массива и обрабатываем их шаблоном | ||
+ | $lines = Array(); | ||
+ | $block_arr = Array(); | ||
+ | |||
+ | foreach ($limitedItems as $item){ | ||
+ | $line_arr = array(); | ||
+ | |||
+ | $itemId = $item->id; | ||
+ | $line_arr['attribute:id'] = $item->id; | ||
+ | $line_arr['attribute:link'] = $item->link; | ||
+ | $line_arr['xlink:href'] = "upage://" . $itemId; | ||
+ | $line_arr['void:header'] = $lines_arr['name'] = $item->getName(); | ||
+ | $line_arr['node:name'] = $item->getName(); | ||
+ | |||
+ | if($publish_time = $item->getValue('publish_time')){ | ||
+ | $line_arr['attribute:publish_time'] = $publish_time->getFormattedDate("U"); | ||
+ | } | ||
+ | unset($item); | ||
+ | |||
+ | $lines[] = def_module::parseTemplate($template_line, $line_arr, $itemId); | ||
+ | unset($line_arr); | ||
+ | |||
+ | $this->pushEditable("news", "rubric", $itemId); | ||
+ | unset($itemId); | ||
+ | } | ||
+ | |||
+ | if(is_array($parentId)) { | ||
+ | list($parentId) = $parentId; | ||
+ | } | ||
+ | $block_arr['subnodes:items'] = $block_arr['void:lines'] = $lines; | ||
+ | unset($lines); | ||
+ | |||
+ | $block_arr['total'] = $total; | ||
+ | $block_arr['per_page'] = $per_page; | ||
+ | |||
+ | return def_module::parseTemplate($template_block, $block_arr, $parentId); | ||
+ | //если массив $resultItems пустой, то выводим блок шаблона $template_block_empty | ||
+ | }else{ | ||
+ | return $template_block_empty; | ||
+ | } | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | == Применение в xslt == | ||
+ | |||
+ | '''Пример вызова''' | ||
+ | <source lang="xml"> | ||
+ | udata://news/customNewsList/183/ | ||
+ | </source> | ||
+ | '''XML-ответ UData''' | ||
+ | <source lang="xml"> | ||
+ | <udata xmlns:xlink="http://www.w3.org/TR/xlink" module="news" method="customNewsList" generation-time="0.054807"> | ||
+ | <items> | ||
+ | <item id="189" link="/novosti3/novost5/" xlink:href="upage://189" publish_time="1382102520">Новость5</item> | ||
+ | <item id="187" link="/novosti2/novost4/" xlink:href="upage://187" publish_time="1382001240">Новость4</item> | ||
+ | <item id="185" link="/novosti2/novost2/" xlink:href="upage://185" publish_time="1381913100">Новость2</item> | ||
+ | <item id="184" link="/novosti2/novost1/" xlink:href="upage://184" publish_time="1381826640">Новость1</item> | ||
+ | <item id="186" link="/news1/novost3/" xlink:href="upage://186" publish_time="1381742040">Новость3</item> | ||
+ | </items> | ||
+ | <total>5</total> | ||
+ | <per_page>10</per_page> | ||
+ | </udata> | ||
+ | </source> | ||
+ | '''Элементы и атрибуты''' | ||
+ | |||
+ | '''<items>''' | ||
+ | |||
+ | Ветвь, содержащая элементы item — новости. | ||
+ | '''<item>''' | ||
+ | |||
+ | Элемент, описывающий отдельную новость. Значение элемента — название страницы новости. | ||
+ | '''@id''' | ||
+ | |||
+ | Идентификатор страницы новости. | ||
+ | '''@link''' | ||
+ | |||
+ | Ссылка на страницу полного текста новости. | ||
+ | '''@xlink:href''' | ||
+ | |||
+ | Ссылка UPage на страницу полного текста новости. | ||
+ | '''@publish_time''' | ||
+ | |||
+ | Время публикации новости в UNIX TIMESTAMP. | ||
+ | |||
+ | '''<total>''' | ||
+ | |||
+ | Общее количество полученных новостей. | ||
+ | '''<per_page>''' | ||
+ | |||
+ | Количество новостей, отображаемых на странице. | ||
+ | |||
+ | == Применение в tpl == | ||
+ | |||
+ | '''Пример вызова макроса''' | ||
+ | |||
+ | %news customNewsList('183')% | ||
+ | |||
+ | '''Используемые шаблоны''' | ||
+ | |||
+ | Макрос оперирует шаблонами, находящимися в каталоге /tpls/news/. Вывод макроса осуществляется по шаблону, указанному в параметре template. | ||
+ | Вы можете не указывать этот параметр, тогда для вывода будет использован шаблон по умолчанию — default.tpl. | ||
+ | |||
+ | '''Используемые блоки шаблона''' | ||
+ | |||
+ | '''items_block''' | ||
+ | |||
+ | Выводит обрамляющий блок для вывода результатов работы макроса, куда будут подставляться результирующие элементы. Отдельных новости будут отрисованы по блоку item. | ||
+ | |||
+ | %items% | ||
+ | |||
+ | Выводит список страниц, отрисованных с помощью блока item_block. | ||
+ | |||
+ | '''item_block''' | ||
+ | |||
+ | Отвечает за вывод отдельной новости, полученной в результате работы макроса. | ||
+ | |||
+ | '''%id%''' | ||
+ | |||
+ | Выводит id новости. | ||
+ | '''%link%''' | ||
+ | |||
+ | Выводит uri новости. | ||
+ | '''%value%''' | ||
+ | |||
+ | Выводит название новости. | ||
+ | '''%publish_time%''' | ||
+ | |||
+ | Выводит время публикации новости в UNIX TIMESTAMP. | ||
+ | '''%total%''' | ||
+ | |||
+ | Общее количество полученных новостей. | ||
+ | '''%per_page%''' | ||
+ | |||
+ | Выводит количество новостей, отображаемых на странице. | ||
+ | |||
+ | '''items_block_empty''' | ||
+ | |||
+ | Используется вместо блока items_block в том случае, если новостей, которые необходимо вывести нет. |
Текущая версия на 15:44, 31 октября 2013
Актуально для версии 2.9.1
Содержание
Задача
Иногда необходимо, чтобы одна и та же новость отображалась в нескольких лентах новостей, стандартными средствами можно создавать виртуальные или просто копии новости и перетаскивать их через модуль "Структура" по нужным разделам, а если на сайте сложная структура новостных лент, это не совсем удобно.
Поэтому, нам нужно реализовать функционал, который позволит указывать на странице конкретной новости, в каком разделе её выводить.
Решение
Со стороны административной панели, нам понадобиться создать в типе данных 'Новость' поле типа "ссылка на дерево" с идентификатором 'rubric_link', все эти действия производятся в модуле "Шаблоны данных", в результате мы получим примерно следующее:
Теперь мы можем у каждой новости указать раздел для вывода:
Разделы мы указали, теперь нам понадобиться кастомный макрос, который будет выводить новости из конкретной ленты новостей, плюс новости из всех лент, у которых в поле с идентификатором 'rubric_link' выбрана эта лента новостей. Код данного макроса и его описание даны ниже.
Описание макроса
%news customNewsList()% — выводит новости из выбранной ленты и новости из всех лент новостей, у которых в поле типа "ссылка на дерево" с идентификатором 'rubric_link' указана выбранная лента.
Параметры: news customNewsList($elementPath,[$template = "default", $per_page = false, $ignore_paging = false])
$elementPath
Принимает id или путь до ленты новостей, новости из которой будут выводиться макроса
$template
Принимает имя шаблона, по которому выводится результат макроса. В XSLT-шаблонизаторе игнорируется.
$per_page
Принимает число, которое обозначает максимальное количество новостей. Если этот параметр не указывать, будет взято значение, указанное в настройках модуля «Новости».
$ignore_paging
Принимаем булево значение («1» или «0»), указывающее макросу игнорировать значение текущей страницы списка вывода (параметр http-запроса p). То есть, если указать «1», макрос будет всегда выводить только первую страницу списка новостей
Для применения этого макроса скопируйте содержимое листинга макроса в файл /classes/modules/news/__custom.php, и не забудьте указать имя метода в permissions.custom.php.
Листинг макроса
public function customNewsList($elementPath, $template = "default", $per_page = false, $ignore_paging = false){
//если $per_page не задан, то берем его из настроек модуля Новости
if(!$per_page){
(int) $per_page = $this->per_page;
}
//загружаем шаблон
list($template_block, $template_block_empty, $template_line) = def_module::loadTemplates("news/".$template, "items_block", "items_block_empty", "item_block");
//берем параметр 'p'
$currPageNum = (int) getRequest('p');
//если в $elementPath указан путь до ленты, то вытаскиваем её id
$parentId = $this->analyzeRequiredPath($elementPath);
//если такой страницы нет, то кидаем исключение
if($parentId === false){
throw new publicException(getLabel('error-page-does-not-exist', null, $elementPath));
}
//задаем массив $itemsFromCurrLent, куда попадут новости из выбранной ленты
$itemsFromCurrLent = array();
//получаем эти новости:
$currLentItems = new selector('pages');
$currLentItems->types('object-type')->name('news', 'item');
$currLentItems->where('hierarchy')->page($parentId);
//записываем их в массив $itemsFromCurrLent
$itemsFromCurrLent = $currLentItems->result();
unset($currLentItems);
//задаем массив $itemsWithCurrLink, куда попадут новости из всех лент, у которых задана выбранная лента
$itemsWithCurrLink = array();
//получаем эти новости
$allLentsItems = new selector('pages');
$allLentsItems->types('object-type')->name('news', 'item');
$allLentsItems->where('rubric_link')->equals($parentId);
//записываем их в массив $itemsWithCurrLink
$itemsWithCurrLink = $allLentsItems->result();
unset($allLentsItems);
//задаем результирующий массив новостей
$resultItems = array();
//помещаем в него все элементы из массива $itemsFromCurrLent и $itemsWithCurrLink, удаляем дубли.
$resultItems = array_unique(array_merge($itemsFromCurrLent, $itemsWithCurrLink));
unset($itemsFromCurrLent, $itemsWithCurrLink);
//задаем числовую переменную $total, содержащую количество элементов массива $resultItems
(int) $total = count($resultItems);
//если массив не пустой, то
if($total>0){
//сортируем элементы массива $resultItems по полю publish_time, то есть по дате публикации
usort($resultItems, function($a, $b){
$a_publishTime = $a->getValue('publish_time');
$b_publishTime = $b->getValue('publish_time');
if ($a_publishTime == $b_publishTime) {
return 0;
}
return ($a_publishTime > $b_publishTime) ? -1 : 1;
});
//задаем массив $limitedItems
$limitedItems = array();
//если $ignore_paging = true, то массив $limitedItems становится результирующим
if($ignore_paging){
$limitedItems = $resultItems;
//если $ignore_paging = false, то оставляем в результирующем массиве элементы, учитывая параметр 'p' и $per_page
}else{
$offset = $currPageNum * $per_page;
$limitedItems = array_slice($resultItems, $offset, $per_page);
}
unset($resultItems);
//перебираем элементы массива и обрабатываем их шаблоном
$lines = Array();
$block_arr = Array();
foreach ($limitedItems as $item){
$line_arr = array();
$itemId = $item->id;
$line_arr['attribute:id'] = $item->id;
$line_arr['attribute:link'] = $item->link;
$line_arr['xlink:href'] = "upage://" . $itemId;
$line_arr['void:header'] = $lines_arr['name'] = $item->getName();
$line_arr['node:name'] = $item->getName();
if($publish_time = $item->getValue('publish_time')){
$line_arr['attribute:publish_time'] = $publish_time->getFormattedDate("U");
}
unset($item);
$lines[] = def_module::parseTemplate($template_line, $line_arr, $itemId);
unset($line_arr);
$this->pushEditable("news", "rubric", $itemId);
unset($itemId);
}
if(is_array($parentId)) {
list($parentId) = $parentId;
}
$block_arr['subnodes:items'] = $block_arr['void:lines'] = $lines;
unset($lines);
$block_arr['total'] = $total;
$block_arr['per_page'] = $per_page;
return def_module::parseTemplate($template_block, $block_arr, $parentId);
//если массив $resultItems пустой, то выводим блок шаблона $template_block_empty
}else{
return $template_block_empty;
}
}
Применение в xslt
Пример вызова
udata://news/customNewsList/183/
XML-ответ UData
<udata xmlns:xlink="http://www.w3.org/TR/xlink" module="news" method="customNewsList" generation-time="0.054807">
<items>
<item id="189" link="/novosti3/novost5/" xlink:href="upage://189" publish_time="1382102520">Новость5</item>
<item id="187" link="/novosti2/novost4/" xlink:href="upage://187" publish_time="1382001240">Новость4</item>
<item id="185" link="/novosti2/novost2/" xlink:href="upage://185" publish_time="1381913100">Новость2</item>
<item id="184" link="/novosti2/novost1/" xlink:href="upage://184" publish_time="1381826640">Новость1</item>
<item id="186" link="/news1/novost3/" xlink:href="upage://186" publish_time="1381742040">Новость3</item>
</items>
<total>5</total>
<per_page>10</per_page>
</udata>
Элементы и атрибуты
<items>
Ветвь, содержащая элементы item — новости.
<item>
Элемент, описывающий отдельную новость. Значение элемента — название страницы новости.
@id
Идентификатор страницы новости.
@link
Ссылка на страницу полного текста новости.
@xlink:href
Ссылка UPage на страницу полного текста новости.
@publish_time
Время публикации новости в UNIX TIMESTAMP.
<total>
Общее количество полученных новостей.
<per_page>
Количество новостей, отображаемых на странице.
Применение в tpl
Пример вызова макроса
%news customNewsList('183')%
Используемые шаблоны
Макрос оперирует шаблонами, находящимися в каталоге /tpls/news/. Вывод макроса осуществляется по шаблону, указанному в параметре template. Вы можете не указывать этот параметр, тогда для вывода будет использован шаблон по умолчанию — default.tpl.
Используемые блоки шаблона
items_block
Выводит обрамляющий блок для вывода результатов работы макроса, куда будут подставляться результирующие элементы. Отдельных новости будут отрисованы по блоку item.
%items%
Выводит список страниц, отрисованных с помощью блока item_block.
item_block
Отвечает за вывод отдельной новости, полученной в результате работы макроса.
%id%
Выводит id новости.
%link%
Выводит uri новости.
%value%
Выводит название новости.
%publish_time%
Выводит время публикации новости в UNIX TIMESTAMP.
%total%
Общее количество полученных новостей.
%per_page%
Выводит количество новостей, отображаемых на странице.
items_block_empty
Используется вместо блока items_block в том случае, если новостей, которые необходимо вывести нет.