Вывод новостей по сюжету — различия между версиями

Материал из Umicms
Перейти к:навигация, поиск
 
(не показано 14 промежуточных версий 2 участников)
Строка 1: Строка 1:
[[category:Написание кастомных макросов]][[Категория: Верстка в XSLT]][[Категория:Верстка в TPL]][[Категория:API]][[Категория:Модуль_Новости]]
+
[[category:Написание кастомных макросов]][[Категория:API]][[Категория:Модуль_Новости]]
'''Актуально для версии 2.9.1'''
+
'''Актуально для версии 2.11'''
  
 
== Задача ==
 
== Задача ==
Строка 11: Строка 11:
 
Есть макрос %news related_links()%, который выводит список последних новостей, связанных по сюжету с указанной новостью.
 
Есть макрос %news related_links()%, который выводит список последних новостей, связанных по сюжету с указанной новостью.
 
А если мы просто захотим вывести все новости, у которых указан определенный сюжеты или сюжеты, то такого макрос в системе нет.
 
А если мы просто захотим вывести все новости, у которых указан определенный сюжеты или сюжеты, то такого макрос в системе нет.
Можно конечно указать у новости тег и вывести все страницы с указанным тегом, но это не самое красивое и удобное решение,  
+
Можно конечно указать у новости тег и вывести все страницы с указанным тегом, но это не самое красивое и удобное решение,
 
особенно, если используете теги для других нужд.
 
особенно, если используете теги для других нужд.
  
Строка 33: Строка 33:
  
 
%news subjectsList()% — выводит новости, относящиеся к выбранн(ому)ым сюже(ту)там.
 
%news subjectsList()% — выводит новости, относящиеся к выбранн(ому)ым сюже(ту)там.
 +
 +
'''Параметры: news subjectsList($id, [$template = 'default', $mode = 'and', $per_page = 0, $ignore_paging = false])'''
 +
 +
'''$id'''
 +
  Принимает идентификатор сюжета, поле, которое мы создали ранее. Можно указать несколько идентификатором через знак ";".
 +
'''$template '''
 +
  Принимает имя шаблона, по которому выводится результат макроса, если его не указать, то он принимает значение 'default' В XSLT-шаблонизаторе игнорируется.
 +
'''$mode'''
 +
  Принимаем два значения 'and' и 'or', когда выбрано 'and', макрос возвращает новости, среди сюжетов которых присутствуют указанные в параметре $id сюжеты,
 +
  когда выбрано 'or', макрос возвращаем новости, у которых сюжеты полностью совпадают с указанными в параметре $id сюжетами. Если параметр не указать, то
 +
  будет по-умолчанию равен 'and'.
 +
'''$per_page '''
 +
  Принимает число, которое обозначает максимальное количество новостей. Если этот параметр не указывать, будет взято значение, указанное в настройках модуля «Новости».
 +
'''$ignore_paging '''
 +
  Принимаем булево значение («1» или «0»), указывающее макросу игнорировать значение текущей страницы списка вывода (параметр http-запроса p). То есть, если указать «1»,
 +
  макрос будет всегда выводить только первую страницу списка новостей.
 +
 +
Для применения этого макроса скопируйте содержимое листинга макроса в файл /classes/modules/news/__custom.php, и не забудьте указать имя метода в permissions.custom.php, то
 +
есть создать такой файл в директории /classes/modules/news/ со следующим содержимым:
 +
 +
<source lang="php">
 +
<?php
 +
  $permissions = array(
 +
'view' => array(
 +
'subjectsList'
 +
)
 +
);
 +
?>
 +
</source>
  
 
== Листинг макроса ==
 
== Листинг макроса ==
 +
 +
<source lang="php">
 +
public function subjectsList($id = '', $template = '', $mode = '', $per_page = 0, $ignore_paging = false){
 +
if(!$id){
 +
throw new publicException('<b>В макросе news subjectsList() необходимо указать первый параметр $id</b>');
 +
}
 +
 +
$id = preg_replace('/\s/', '', $id);
 +
$parsId = array();
 +
$parsId = explode(';',$id);
 +
unset($id);
 +
 +
if(!$template){
 +
$template = 'default';
 +
}
 +
 +
if(!$mode){
 +
$mode = 'and';
 +
}
 +
 +
if(!$per_page){
 +
(int) $per_page = $this->per_page;
 +
}
 +
 +
if(!is_numeric($per_page)){
 +
throw new publicException('<b>В макрос news subjectsList() передано некорректно значение номера страницы</b>');
 +
}
 +
 +
list($template_block, $template_block_empty, $template_line) = def_module::loadTemplates("news/".$template, "items_block", "items_block_empty", "item_block");
 +
 +
$currPageNum = getRequest('p');
 +
 +
$resultSubjects = array();
 +
$subjectsQuery = new selector('objects');
 +
$subjectsQuery->types('object-type')->name('news', 'subject');
 +
$subjectsQuery->where('subject-id')->equals($parsId);
 +
$resultSubjects = $subjectsQuery->result();
 +
unset($subjectsQuery, $parsId);
 +
 +
(int) $countSubject = count($resultSubjects);
 +
 +
if($countSubject === 0){
 +
throw new publicException('<b>Сюжеты с переданными id не найдены</b>');
 +
}
 +
 +
$resultItems = array();
 +
$itemsQuery = new selector('pages');
 +
$itemsQuery->types('object-type')->name('news', 'item');
 +
$itemsQuery->option('or-mode')->field('subjects');
 +
$itemsQuery->where('subjects')->equals($resultSubjects);
 +
 +
switch ($mode){
 +
case 'or':
 +
$allItems = array();
 +
$allItems = $itemsQuery->result();
 +
 +
if(count($allItems) === 0){
 +
return $template_block_empty;
 +
}
 +
 +
foreach($allItems as $oneItem){
 +
$items = array();
 +
$items[] = $oneItem->getValue('subjects');
 +
 +
foreach($items as $item){
 +
$test = array();
 +
$test = array_diff($resultSubjects, $item);
 +
 +
if (count($test) === 0){
 +
 +
if($countSubject > 1){
 +
$resultItems[] = $oneItem;
 +
 +
}elseif(count($item) === 1){
 +
$resultItems[] = $oneItem;
 +
}
 +
}
 +
}
 +
}
 +
unset($itemsQuery, $allItems, $oneItem, $items, $item, $test, $resultSubjects, $countSubject);
 +
 +
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;
 +
});
 +
 +
if(intval($ignore_paging) === 0){
 +
$offset = 0;
 +
$offset = intval($currPageNum) * intval($per_page);
 +
 +
$resultItems = array_slice($resultItems, $offset, $per_page);
 +
unset($offset);
 +
}
 +
$total = 0;
 +
$total = count($resultItems);
 +
 +
break;
 +
 +
case 'and':
 +
unset($resultSubjects, $countSubject);
 +
 +
$itemsQuery->order('publish_time')->desc();
 +
 +
if(intval($ignore_paging) === 0){
 +
$offset  = 0;
 +
$offset  = intval($currPageNum) * intval($per_page);
 +
$itemsQuery->limit($offset , intval($per_page));
 +
}
 +
$total = 0;
 +
$total = $itemsQuery->length();
 +
$resultItems = $itemsQuery->result();
 +
unset($itemsQuery, $offset);
 +
 +
break;
 +
 +
default:
 +
throw new publicException('<b>В макросе news subjectsList() необходимо указать корректное значение параметра $mode (or или and)</b>');
 +
}
 +
if($total === 0){
 +
return $template_block_empty;
 +
}else{
 +
$lines = Array();
 +
$block_arr = Array();
 +
 +
foreach ($resultItems as $resultItem){
 +
$line_arr = array();
 +
 +
$itemId = $resultItem->id;
 +
$line_arr['attribute:id'] = $itemId;
 +
$line_arr['attribute:link'] = $resultItem->link;
 +
$line_arr['xlink:href'] = "upage://" . $itemId;
 +
$line_arr['void:header'] = $lines_arr['name'] = $resultItem->getName();
 +
$line_arr['node:name'] =  $resultItem->getName();
 +
 +
if($publish_time = $resultItem->getValue('publish_time')){
 +
$line_arr['attribute:publish_time'] = $publish_time->getFormattedDate("U");
 +
}
 +
unset($resultItems, $resultItem);
 +
 +
$lines[] = def_module::parseTemplate($template_line, $line_arr, $itemId);
 +
unset($line_arr);
 +
 +
$this->pushEditable("news", "item", $itemId);
 +
unset($itemId);
 +
}
 +
$block_arr['subnodes:items'] = $block_arr['void:lines'] = $lines;
 +
unset($lines);
 +
 +
$block_arr['total'] = $total;
 +
$block_arr['per_page'] = $per_page;
 +
unset($total, $per_page);
 +
 +
return def_module::parseTemplate($template_block, $block_arr);
 +
}
 +
}
 +
</source>
  
 
== Применение в xslt ==
 
== Применение в xslt ==
 +
 +
'''Пример вызова'''
 +
 +
<source lang="xml">
 +
udata/news/subjectsList/(eco;pol)//or/3/1/
 +
</source>
 +
 +
'''XML-ответ UData'''
 +
 +
<source lang="xml">
 +
<udata xmlns:xlink="http://www.w3.org/TR/xlink" module="news" method="subjectsList" generation-time="0.075129">
 +
  <items>
 +
      <item id="191" link="/novosti3/novost7/" xlink:href="upage://191" publish_time="1382102520">Новость7</item>
 +
      <item id="190" link="/novosti3/novost6/" xlink:href="upage://190" publish_time="1382102520">Новость6</item>
 +
      <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>
 +
      <item id="78" link="/news1/speshite_akciya/" xlink:href="upage://78" publish_time="1261279500">Новость9</item>
 +
      <item id="79" link="/news1/ne_speshite_ona_zakonchilas/" xlink:href="upage://79" publish_time="1259020800">Новость8</item>
 +
  </items>
 +
  <total>9</total>
 +
  <per_page>3</per_page>
 +
</udata>
 +
</source>
 +
 +
'''Элементы и атрибуты'''
 +
 +
'''<items>'''
 +
 +
    Ветвь, содержащая элементы item — новости.
 +
'''<item>'''
 +
 +
    Элемент, описывающий отдельную новость. Значение элемента — название страницы новости.
 +
'''@id'''
 +
 +
    Идентификатор страницы новости.
 +
'''@link'''
 +
 +
    Ссылка на страницу полного текста новости.
 +
'''@xlink:href'''
 +
 +
    Ссылка UPage на страницу полного текста новости.
 +
'''@publish_time'''
 +
 +
    Время публикации новости в UNIX TIMESTAMP.
 +
'''<total>'''
 +
 +
    Общее количество полученных новостей.
 +
'''<per_page>'''
 +
 +
    Количество новостей, отображаемых на странице.
  
 
== Применение в tpl ==
 
== Применение в tpl ==
 +
 +
'''Пример вызова макроса'''
 +
 +
<source lang="php">
 +
%news subjectsList('eco;pol', 'subjects', 'or', '10', '1')%
 +
</source>
 +
 +
Чтобы вызов сработал, нужно создать файл /tpls/news/subjects.tpl с таким содержимым:
 +
 +
<source lang="php">
 +
<?php
 +
$FORMS = Array();
 +
 +
$FORMS['item_block'] = <<<END
 +
<li>
 +
<p>Новость<p>
 +
</li>
 +
END;
 +
 +
$FORMS['items_block'] = <<<END
 +
<ul>%items%</ul>
 +
END;
 +
 +
$FORMS['items_block_empty'] = <<<END
 +
<p>Нет таких новостей.</p>
 +
END;
 +
?>
 +
</source>
 +
 +
'''Используемые шаблоны'''
 +
 +
Макрос оперирует шаблонами, находящимися в каталоге /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 в том случае, если новостей, которые необходимо вывести нет.

Текущая версия на 08:21, 2 февраля 2016

Актуально для версии 2.11

Задача

Стандартно в UMI.CMS у типа данных "Новость" есть поле "Сюжеты", также есть одноименный тип данных. У любой новости можно выбрать некоторое количество сюжетов:

Subject1.png

Есть макрос %news related_links()%, который выводит список последних новостей, связанных по сюжету с указанной новостью. А если мы просто захотим вывести все новости, у которых указан определенный сюжеты или сюжеты, то такого макрос в системе нет. Можно конечно указать у новости тег и вывести все страницы с указанным тегом, но это не самое красивое и удобное решение, особенно, если используете теги для других нужд.

Решение

В этой статье будет приведен макрос, решающий нашу задачу, но сначала нужно будет внести некоторые изменения в тип данных "Сюжет публикации", добавим в этот тип данных следующую группу полей и поле, обязательно с теми же идентификаторами:

Subject2.png

Для использования, приведённого ниже, макроса, нам нужно будет заполнить созданное поле у элементов справочника "Сюжет публикации", обязательно латинскими буквами, например так:

Subject3.png

А потом можно будет выбрать эти значения у новостей:

Subject4.png

Описание макроса

%news subjectsList()% — выводит новости, относящиеся к выбранн(ому)ым сюже(ту)там.

Параметры: news subjectsList($id, [$template = 'default', $mode = 'and', $per_page = 0, $ignore_paging = false])

$id

  Принимает идентификатор сюжета, поле, которое мы создали ранее. Можно указать несколько идентификатором через знак ";".

$template

  Принимает имя шаблона, по которому выводится результат макроса, если его не указать, то он принимает значение 'default' В XSLT-шаблонизаторе игнорируется.

$mode

  Принимаем два значения 'and' и 'or', когда выбрано 'and', макрос возвращает новости, среди сюжетов которых присутствуют указанные в параметре $id сюжеты,
  когда выбрано 'or', макрос возвращаем новости, у которых сюжеты полностью совпадают с указанными в параметре $id сюжетами. Если параметр не указать, то
  будет по-умолчанию равен 'and'.

$per_page

  Принимает число, которое обозначает максимальное количество новостей. Если этот параметр не указывать, будет взято значение, указанное в настройках модуля «Новости».

$ignore_paging

  Принимаем булево значение («1» или «0»), указывающее макросу игнорировать значение текущей страницы списка вывода (параметр http-запроса p). То есть, если указать «1»,
  макрос будет всегда выводить только первую страницу списка новостей.

Для применения этого макроса скопируйте содержимое листинга макроса в файл /classes/modules/news/__custom.php, и не забудьте указать имя метода в permissions.custom.php, то есть создать такой файл в директории /classes/modules/news/ со следующим содержимым:

<?php
   $permissions = array(
	'view' => array(
			'subjectsList'
		)
	);
?>

Листинг макроса

public function subjectsList($id = '', $template = '', $mode = '', $per_page = 0, $ignore_paging = false){
	if(!$id){
		throw new publicException('<b>В макросе news subjectsList() необходимо указать первый параметр $id</b>');
	}

	$id = preg_replace('/\s/', '', $id);
	$parsId = array();
	$parsId = explode(';',$id);
	unset($id);

	if(!$template){
		$template = 'default';
	}

	if(!$mode){
		$mode = 'and';
	}

	if(!$per_page){
		(int) $per_page = $this->per_page;
	}

	if(!is_numeric($per_page)){
		throw new publicException('<b>В макрос news subjectsList() передано некорректно значение номера страницы</b>');
	}

	list($template_block, $template_block_empty, $template_line) = def_module::loadTemplates("news/".$template, "items_block", "items_block_empty", "item_block");

	$currPageNum = getRequest('p');

	$resultSubjects = array();
	$subjectsQuery = new selector('objects');
	$subjectsQuery->types('object-type')->name('news', 'subject');
	$subjectsQuery->where('subject-id')->equals($parsId);
	$resultSubjects = $subjectsQuery->result();
	unset($subjectsQuery, $parsId);

	(int) $countSubject = count($resultSubjects);

	if($countSubject === 0){
		throw new publicException('<b>Сюжеты с переданными id не найдены</b>');
	}

	$resultItems = array();
	$itemsQuery = new selector('pages');
	$itemsQuery->types('object-type')->name('news', 'item');
	$itemsQuery->option('or-mode')->field('subjects');
	$itemsQuery->where('subjects')->equals($resultSubjects);

	switch ($mode){
		case 'or':
			$allItems = array();
			$allItems = $itemsQuery->result();

			if(count($allItems) === 0){
				return $template_block_empty;
			}

			foreach($allItems as $oneItem){
				$items = array();
				$items[] = $oneItem->getValue('subjects');

				foreach($items as $item){
					$test = array();
					$test = array_diff($resultSubjects, $item);

					if (count($test) === 0){

						if($countSubject > 1){
							$resultItems[] = $oneItem;

						}elseif(count($item) === 1){
							$resultItems[] = $oneItem;
						}
					}
				}
			}
			unset($itemsQuery, $allItems, $oneItem, $items, $item, $test, $resultSubjects, $countSubject);

			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;
			});

			if(intval($ignore_paging) === 0){
				$offset = 0;
				$offset = intval($currPageNum) * intval($per_page);

				$resultItems = array_slice($resultItems, $offset, $per_page);
				unset($offset);
			}
			$total = 0;
			$total = count($resultItems);

		break;

		case 'and':
			unset($resultSubjects, $countSubject);

			$itemsQuery->order('publish_time')->desc();

			if(intval($ignore_paging) === 0){
				$offset  = 0;
				$offset  = intval($currPageNum) * intval($per_page);
				$itemsQuery->limit($offset , intval($per_page));
			}
			$total = 0;
			$total = $itemsQuery->length();
			$resultItems = $itemsQuery->result();
			unset($itemsQuery, $offset);

		break;

		default:
			throw new publicException('<b>В макросе news subjectsList() необходимо указать корректное значение параметра $mode (or или and)</b>');
	}
	if($total === 0){
		return $template_block_empty;
	}else{
		$lines = Array();
		$block_arr = Array();

		foreach ($resultItems as $resultItem){
			$line_arr = array();

			$itemId = $resultItem->id;
			$line_arr['attribute:id'] = $itemId;
			$line_arr['attribute:link'] = $resultItem->link;
			$line_arr['xlink:href'] = "upage://" . $itemId;
			$line_arr['void:header'] = $lines_arr['name'] = $resultItem->getName();
			$line_arr['node:name'] =  $resultItem->getName();

			if($publish_time = $resultItem->getValue('publish_time')){
				$line_arr['attribute:publish_time'] = $publish_time->getFormattedDate("U");
			}
			unset($resultItems, $resultItem);

			$lines[] = def_module::parseTemplate($template_line, $line_arr, $itemId);
			unset($line_arr);

			$this->pushEditable("news", "item", $itemId);
			unset($itemId);
		}
		$block_arr['subnodes:items'] = $block_arr['void:lines'] = $lines;
		unset($lines);

		$block_arr['total'] = $total;
		$block_arr['per_page'] = $per_page;
		unset($total, $per_page);

		return def_module::parseTemplate($template_block, $block_arr);
	}
}

Применение в xslt

Пример вызова

udata/news/subjectsList/(eco;pol)//or/3/1/

XML-ответ UData

<udata xmlns:xlink="http://www.w3.org/TR/xlink" module="news" method="subjectsList" generation-time="0.075129">
   <items>
      <item id="191" link="/novosti3/novost7/" xlink:href="upage://191" publish_time="1382102520">Новость7</item>
      <item id="190" link="/novosti3/novost6/" xlink:href="upage://190" publish_time="1382102520">Новость6</item>
      <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>
      <item id="78" link="/news1/speshite_akciya/" xlink:href="upage://78" publish_time="1261279500">Новость9</item>
      <item id="79" link="/news1/ne_speshite_ona_zakonchilas/" xlink:href="upage://79" publish_time="1259020800">Новость8</item>
   </items>
   <total>9</total>
   <per_page>3</per_page>
</udata>

Элементы и атрибуты

<items>

   Ветвь, содержащая элементы item — новости.

<item>

   Элемент, описывающий отдельную новость. Значение элемента — название страницы новости.

@id

   Идентификатор страницы новости.

@link

   Ссылка на страницу полного текста новости.

@xlink:href

   Ссылка UPage на страницу полного текста новости.

@publish_time

   Время публикации новости в UNIX TIMESTAMP.

<total>

   Общее количество полученных новостей.

<per_page>

   Количество новостей, отображаемых на странице.

Применение в tpl

Пример вызова макроса

%news subjectsList('eco;pol', 'subjects', 'or', '10', '1')%

Чтобы вызов сработал, нужно создать файл /tpls/news/subjects.tpl с таким содержимым:

<?php
$FORMS = Array();

$FORMS['item_block'] = <<<END
<li>
	<p>Новость<p>
</li>
END;

$FORMS['items_block'] = <<<END
<ul>%items%</ul>
END;

$FORMS['items_block_empty'] = <<<END
<p>Нет таких новостей.</p>
END;
?>

Используемые шаблоны

Макрос оперирует шаблонами, находящимися в каталоге /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 в том случае, если новостей, которые необходимо вывести нет.