Руководство по созданию плагина для v.0.4 на примере "Лента друзей"

Предисловие

Что такое плагины — читайте здесь.
По мотивам этого, выполняю обещание — привожу руководство как подобное сделать в виде плагина. Всех тонкостей в одной заметке не расскажешь, но основное постараюсь изложить.

Примечание. Долго не публиковал этот материал, держал в черновиках — на случай «мало-ли-что-измениться». Но думаю, кардинальных изменений в механизме плагинов в ближайшем релизе уже не будет.

Пошаговая схема:
1. Создаем директорию и основные файлы плагина

Назовем плагин Friends Feed, но для краткости в названиях файлов и директорий будем использовать просто friends. Поэтому создаем директорию friends в директории plugins.

Вкладываем туда файлы PluginFriends.class.php и plugin.xml

PluginFriend.class.php

<?php
/**
 * Плагин предназначен для выведения пользователю списка топиков его друзей
 * 
 * by Alex Kachayev <kachayev@gmail.com>
 */
class PluginFriends extends Plugin {
	/**
	 * Активация плагина
	 * В принципе, здесь нам делать ничего не нужно
	 */
	public function Activate() {
		return true;
	}
	
	/**
	 * Инициализация плагина
	 */
	public function Init() {
		$this->Viewer_AddMenu('blog',Plugin::GetTemplatePath(__CLASS__).'/menu.blog.tpl');
	}
	
	/**
	 * Деактивация плагина
	 * В принципе, тут тоже ничего не нужно делать
	 */
	public function Deactivate() {
		return true;
	}
}


Примечание. Функции Activate(), Deactivate() можно было и не создавать, добавил просто чтоб напомнить о возможности их использования. В функции Init() «всплывает» достаточно интересный момент — замена стандартного меню, но об этом мы поговорим чуть позже.

Plugin.xml
<?xml version="1.0" encoding="UTF-8"?>
<plugin>
	<name>
		<lang name="default">Friends Feed</lang>
		<lang name="russian">Лента Друзей</lang>
	</name>
	<author>
		<lang name="default">Alex Kachayev</lang>
		<lang name="russian">Алексей Качаев</lang>
	</author>
	<homepage>http://kachayev.ru/</homepage>
	<version>0.8.1</version>
	<requires>
		<livestreet>0.4.0</livestreet>
		<plugins>

		</plugins>
	</requires>
	<description>
		<lang name="default">Show topics that are wrote by your friends.</lang>
		<lang name="russian">Вывод записей друзей отдельной лентой.</lang>
	</description>
	<delegate>
		<module></module>
		<action></action>
		<template></template>
		<entity></entity>
	</delegate>
</plugin>


Если все правильно сделали, то с этого момента плагин существует и формально функционирует. Т.е. зайдя в панель управления плагинами (/admin/plugins) вы уже можете увидеть новый «пункт» и произвести активацию\деактивацию. Правда нам это пока ничего не даст, ибо функционала еще не предусмотрено =)

2. Роутинг и конфигурация

Учтем, что лента друзей должна быть доступна по определенному URL. Например, /friends. Значит нам нужно добавить новое правило роутинга. Сделать это нужно в файле конфигурации плагина. Создаем директорию /friends/config/ и вкладываем туда файл config.php — эта конфигурация будет автоматически загружена, если плагин активирован администратором проекта.

Пока от нас требуется только указать новый «пункт» роутинга. Сделаем это вызовом Config::Set(). Для демонстрации работы конфигурации плагина, я также добавлю параметр работы плагина — количество выводимых записей на страницу (в учебных целях, в рабочем проекте я бы воспользовался значением этого параметра для остальных экшенов).

config.php:

/**
 * Плагин предназначен для выведения пользователю списка топиков его друзей
 * 
 * by Alex Kachayev <kachayev@gmail.com>
 */

$config['per_page']   = 10;  // Число топиков на одну страницу ленты

Config::Set('router.page.friends', 'PluginFriends_ActionFeed');

return $config;


3. Action для вывода записей и модуль

Для начала сделаем модуль для получения записей, написанных друзьями. В папке /plugins/friends/ создаем директорию /classes/modules/friends и вкладываем туда файл Friends.class.php:


<?php

class PluginFriends_Friends extends Module {
		public function Init() {
			
		}
        /**
         * Получает число новых топиков в ленте друзей
         *
         * @return array
         */
        public function GetCountTopicsFriendsNew($sUserId) {
        	$sDate=date("Y-m-d H:00:00",time()-Config::Get('module.topic.new_time'));
			$aFriends = $this->User_GetUsersFriend($sUserId);
        	
        	if(!is_array($aFriends) or count($aFriends)==0) {
        		return 0;
        	}
        	
        	$aFilter=array(
	        	'user_id' => array_keys($aFriends),
	        	'topic_publish' => 1,
	        	'topic_new' => $sDate,
        	);

        	$s=serialize($aFilter);
        	if (false === ($data = $this->Cache_Get("topic_friend_count_{$s}"))) {
        		$data = $this->Topic_GetCountTopicsByFilter($aFilter);
        		$this->Cache_Set($data, "topic_friend_count_{$s}", array('topic_update','topic_new'), 60*5);
        	}
        	return  $data;
        }
        /**
         * Получает все топики в ленте друзей
         *
         * @return array
         */
        public function GetTopicsFriends($sUserId,$iPage,$iPerPage) {
        	/**
        	 * Получаем идентификаторы друзей пользователя
        	 */
        	$aFriends = $this->User_GetUsersFriend($sUserId);
        	
        	if(!is_array($aFriends) or count($aFriends)==0) {
        		return array('count'=>0, 'collection'=>array());
        	}
        	
        	$aFilter=array(
	        	'user_id' => array_keys($aFriends),
	        	'topic_publish' => 1,
	        	'blog_type' => array('open','personal')
        	);
        	
        	return $this->Topic_GetTopicsByFilter($aFilter,$iPage,$iPerPage);
        }
        
        public function Shutdown() {
        	
        }
}
?>

Как видите, в этом файле, мы создаем новый модуль PluginFriends_Friends, предназначенный для: а) получения списка топиков в ленте друзей, б) получения количество топиков в ленте друзей. По сути ничего необычно, единственное отличие от простого модуля — приставка в названии с указанием на плагин Friends.

Создаем экшен для обработки вывода записей Ленты Друзей. Для этого в директории /friends/classes/ создаем директорию actions и вкладываем туда файл ActionFeed.class.php:


<?php

class PluginFriends_ActionFeed extends ActionPlugin {
        /**
         * Главное меню
         *
         * @var unknown_type
         */
        protected $sMenuHeadItemSelect='blog';
        /**
         * Меню
         *
         * @var unknown_type
         */
        protected $sMenuItemSelect='index';
        /**
         * Субменю
         *
         * @var unknown_type
         */
        protected $sMenuSubItemSelect='friends';
        /**
         * Число новых топиков
         *
         * @var unknown_type
         */
        protected $iCountTopicsNew=0;
        /**
         * Число новых топиков в ленте друзей
         *
         * @var unknown_type
         */
        protected $iCountTopicsFriendsNew=0;
        /**
         * Число новых топиков в коллективных блогах
         *
         * @var unknown_type
         */
        protected $iCountTopicsCollectiveNew=0;
        /**
         * Число новых топиков в персональных блогах
         *
         * @var unknown_type
         */
        protected $iCountTopicsPersonalNew=0;
        
        /**
         * Инициализация
         *
         */
        public function Init() {                        
        	$this->Viewer_AddBlocks('right',array('stream','tags','blogs'));
        	/**
        	 * Подсчитываем новые топики
        	 */

        	$this->iCountTopicsCollectiveNew=$this->Topic_GetCountTopicsCollectiveNew();
       		$this->iCountTopicsPersonalNew=$this->Topic_GetCountTopicsPersonalNew();
        	$this->iCountTopicsFriendsNew=$this->PluginFriends_Friends_GetCountTopicsFriendsNew($this->User_GetUserCurrent()->getId());
        	$this->iCountTopicsNew=$this->iCountTopicsCollectiveNew+$this->iCountTopicsPersonalNew;
        }
        /**
         * Регистрация евентов
         *
         */
        protected function RegisterEvent() {
                $this->AddEventPreg('/^(page(\d+))?$/i','EventFriends');
        }

        
        /**********************************************************************************
         ************************ РЕАЛИЗАЦИЯ ЭКШЕНА ***************************************
         **********************************************************************************
         */
        
        /**
         * Реализация евента
         *
         */
        protected function EventFriends() {
                /**
                 * Меню
                 */
                $this->sMenuSubItemSelect='friends';
                /**
                 * Передан ли номер страницы
                 */
                $iPage=$this->GetEventMatch(2) ? $this->GetEventMatch(2) : 1;
                /**
                 * Получаем список топиков
                 */                                     
                $aResult=$this->PluginFriends_Friends_GetTopicsFriends($this->User_GetUserCurrent()->getId(),1,10);
                $aTopics=$aResult['collection'];
                /**
                 * Формируем постраничность
                 */
                $aPaging=$this->Viewer_MakePaging($aResult['count'],$iPage,10,4,Router::GetPath('friends'));
                /**
                 * Загружаем переменные в шаблон
                 */
                $this->Viewer_Assign('aTopics',$aTopics);
                $this->Viewer_Assign('aPaging',$aPaging);               
                /**
                 * Устанавливаем шаблон вывода
                 */
                $this->SetTemplateAction('friends');
        }
        /**
         * При завершении экшена загружаем переменные в шаблон
         *
         */
        public function EventShutdown() {
        	$this->Viewer_Assign('sMenuHeadItemSelect',$this->sMenuHeadItemSelect);
        	$this->Viewer_Assign('sMenuItemSelect',$this->sMenuItemSelect);
        	$this->Viewer_Assign('sMenuSubItemSelect',$this->sMenuSubItemSelect);
        	$this->Viewer_Assign('iCountTopicsNew',$this->iCountTopicsNew);
        	$this->Viewer_Assign('iCountTopicsFriendsNew',$this->iCountTopicsFriendsNew);
        	$this->Viewer_Assign('iCountTopicsCollectiveNew',$this->iCountTopicsCollectiveNew);
        	$this->Viewer_Assign('iCountTopicsPersonalNew',$this->iCountTopicsPersonalNew);
        }
}
?>


Это так же самый обычный по виду экшен. Ключевые моменты:
— название PluginFriends_ActionFeed содержит указание на плагин Friends;
— класс экшена унаследован от ActionPlugin.

4. Добавляем шаблоны и пункт меню

Но, сейчас этот экшен работать не будет — нет пока шаблона для работы. С шаблонами есть маленький ньюанс — плагин может использоваться с различными шаблонами (skin). Поэтому чтобы у пользователей не возникало потом проблем с вашим плагином, плагин по умолчанию поддерживает мульти-skin. По умолчанию будет использован skin default. Поскольку наша задача не прорисовать как можно больше вариантов, а посмотреть на общую схему работы, то создаем директорию /templates/skin/default/ и туда вкладываем шаблон нашего экшена: actions/ActionFeed/friends.tpl.

Шаблон максимально простой:

{include file='header.tpl' menu='blog'}
{include file='topic_list.tpl'}
{include file='footer.tpl'}


Осталось последнее — создать пункт меню для попадания в Ленту друзей. Для того, чтобы это сделать нам нужно добавить доп. пункт в меню menu.blog.tpl. Мы сделаем это так: копируем файл menu.blog.tpl из шаблона new движка в директорию /plugins/friends/templates/skin/default/menu.blog.tpl. В этом файле в первую секцию добавим следующий код:


<li {if $sMenuSubItemSelect=='friends'}class="active"{/if}><div><a href="{router page='friends'}">{$aLang.plugin_friends_menu_title}</a>{if $iCountTopicsFriendsNew} +{$iCountTopicsFriendsNew}{/if}</div></li>


Именно это меню подключается в шаблоны в функции инициализации плагина Init() (об этом мы уже говорили):

$this->Viewer_AddMenu('blog',Plugin::GetTemplatePath(__CLASS__).'/menu.blog.tpl');


Последний штрих — языковой файл /templates/language/russian.php с единственной текстовкой:

<?php
/**
 * Русский языковой файл плагина Friends
 */
return array(
	'plugin_friends_menu_title' => 'Лента друзей'
);
?>


5. The End

Собственно и все. Кажется, ничего не забыл. Если остались вопросы — с радостью отвечу.

105 комментариев

avatar
большое спасибо

тоже неплохое изобретение…
avatar
спасибо за мануал, но кат бы в любом случае не помешал :-)
avatar
=) Подрезал.
avatar
Спасибо! Вчера мне этот мануал бы не помешал :)))
avatar
Есть подозрение, что твой пост и подтолкнул Алексея — мол, пора уже, из черновиков на свет белый доставать. :)
avatar
Угу.
avatar
:)))
avatar
Собственно и все. Кажется, ничего не забыл. Если остались вопросы — с радостью отвечу.
Где сам плагин то скачать для 0.4? =)
  • Mmka
  • 0
avatar
Это конструктор-лего. Суть же была не в конечном результате — а в самом процессе создания. После выхода v.0.4 запакую и выложу в каталог модулей.
avatar
Алексей, была такая мелочь еще: конфиг-файл плагина обязательно должен был возвращать массив. Т.е. даже если мне просто надо было роутинг задать через Config::Set('router.page.xxx', ...);, то все равно приходилось возвращать пустой массив. Пишу в прошедшем времени, т.к. не проверял в последних релизах, а так и ставлю пустой массив на выходе. В общем-то, фигня, конечно, но лучше бы это дело как-то проверять при загрузке конфига.
avatar
Пишу в прошедшем времени
В текущем времени все осталось так же: конфиг должен возвращать массив.
avatar
Спасибо за руководство. Пробую.

Вкладываем туда файлы PluginFriend.class.php
PluginFriends.class.php
avatar
языковой файл /templates/language/russian.tpl
russian.php
avatar
Спс, поправил.
avatar
Можно пример, как включить в состав плагина свой блок? Как он должен называться? И где он должен лежать?
avatar
Хороший плаг. Спасибо :)
Хотя, возможно, стоило бы для «руководства» взять плагин пообширнее охватывающий логику плагинов — с делегированием, например — это ж одна из основных фишек. Думаю в ближайшее время надо найти время что-нибудь из поделок для 0,31 переделать в плагин для 0,4, постараюсь захватить что-нибудь что еще не применяли в примерах.
avatar
На самом деле, для меня и делегирование, и все остальное — вполне «обыденно». Мне сложно сказать какая из этих частей функционала является более важной.

Поэтому сделал то, что «заказали» в прошлой публикации =)
avatar
Я тоже не имел ввиду важность какой либо части, я имел ввиду другое — плагины они же на самом деле круче, чем просто способ не правя системные файлы встроить дополнительный функционал, так и надо это показать :) Ту же ленту друзей и на 0,31 можно вообще не затрагивая системные файлы сделать, ну да, только менюшку вписать в шаблон:)
avatar
Спасибо, теперь почти все для себя прояснил.
Один вопрос, как правильно прописывать делегирование внутри ?
avatar
<delegate></delegate>
avatar
«Внутри» — ты имеешь ввиду по коду? Вот так:
$this->Plugin_Delegate()
avatar
внутри xml, тегов


<delegate></delegate>


я не нашел нигде, пробывал
<delegate>
 <action>
  <from>ActionSettings.class.php</from>
  <to>ActionSettings.class.php</to>
 </action>
</delegate>

не помогло. как правильно? :)
avatar
Присоединяюсь к вопросу — не нашел в коде вообще работу с секцией
<delegate></delegate>
считываемой из xml-ки. Невнимательно искал или еще не реализовано?
avatar
Не пойму, почему новые показываются только когда переходишь на ленту?
avatar
Потому что так сделано.
Для вывода количества новых вне экшена Friends, нужно задавать отдельно функционал в каждом из экшенов, где нужно это показывать. Например, с помощью механизма хуков.
avatar
Уже понял, и сделал просто добавив в index и blog, жаль только что нельзя это сделать из плагина.
А про хуки примера не найдется?
avatar
нельзя это сделать из плагина
Можно!
Для этого нужно внутри плагина создать хук, в котором получать количество новых записей в ленте, передавать их во Viewer. И повесить хук на shutdown экшенов index, blog
avatar
Если бы еще знать на что и как ставить хуки)
avatar
я у себя вот так сделал
<?php
/**
 * Хук для плагина Friend Feed
 */
class PluginFriends_HookFriends extends Hook {

        /**
         * Регистрируем хук на action_event_index_after
         *
         * @return void
         */
        public function RegisterHook() {
                if ($this->User_IsAuthorization()) {
                        $this->AddHook("action_event_index_after", "setCountTopicsFriendsNew", __CLASS__);
                }
        }

        /**
         * Получаем количество новых записей в френдленте и передаем их в Viewer
         *
         * @param array $aVars
         */
        function setCountTopicsFriendsNew($aVars) {
                $this->Viewer_Assign('iCountTopicsFriendsNew', $this->PluginFriends_Friends_GetCountTopicsFriendsNew($this->User_GetUserCurrent()->getId()));
        }
}
avatar
Можно немного подробнее, не могу найти где описано событие 'action_event_index_after'? В текущем движке 0.4 не нашел его, однако если такой хук добавить, то он работает в экшене индекс (вот тут большая просьба пояснить как, дефолтное событие?). Если повесить его на 'init_action' — работает в любом экшене.
avatar
разобрался
avatar
а зачем на шутдаун экшена blog его цеплять?
avatar
А у плагина могут быть собственные блоки? Я имею ввиду, можно ли складывать плагиновские блоки вместе с остальными файлами плагина в site.ru/plugins/my_plugin/classes/blocks? Или он обязательно должны быть в site.ru/classes/blocks?
avatar
Да и еще одно интересно, может ли плагин в себе иметь скрипты для обработки ajax запросов. А то у мне пока выдает, что атаковали хакеры.

У кого-нибудь получалось прикрутить к плагину скрипты для обратки ajax запросов?
avatar
Получилось. Надо обязательно среди прочих параметров передавать параметр security_ls_key со значением LIVESTREET_SECURITY_KEY.

Например, добавлять к адресу серверного скрипта:
url+'?security_ls_key='+LIVESTREET_SECURITY_KEY
avatar
я об этом писал здесь
avatar
Можно запускать блоки из директории плагинов. Принцип с именами тот же самый.
avatar
а можно пример? не очень понятно…
avatar
разобрался:
Config::Set('block.rule_bill', array(
  'action'  => array(
    'event', 
    'place'
  ),
  'blocks'  => array(
    'right' => array('stream','tags','blogs','places'=>array('params'=>array('plugin'=>'bill')))
  )
));
avatar
Огромное спасибо за информацию!
avatar
нужно ещё сделать, чтобы гости не видели в меню пункт «Лента друзей» и при переходе по ссылке видели не пустую страницу, а сообщение о ошибке
avatar
1) Ошибка в 49-ой строке файла /engine/classes/ActionPlugin.php
Т.к. $this->GetActionClass() содержит значение из $aConfig['route]['page'][{action}], то вместо
preg_match('/^Plugin([\w]+)_Action([\w]+)$/i',$this->GetActionClass(),$aMatches);
должно быть
preg_match('/^Action([\w]+)$/i',$this->GetActionClass(),$aMatches);
иначе возникает ошибка.
Соответственно появится ошибка и в 54-ой строке, где $aMatches[1] будет содержать не то, что нужно.
То же самое в 95-ой строчке.

2) Обычно делегирование нужно для модифицирования какого-то файла, поэтому он чаще всего лежит по аналогичному пути (в папке плагина) и имеет такое же имя, как и заменяемый аналог. На мой взгляд есть смысл автоматизировать прописывание этого пути.
Чаще всего будет представлять из себя что-то вроде

protected $aDelegates=array(
  'action'  =>array(
    'ActionSettings' => 'PluginProtypes_ActionSettings',
    'ActionRegistration' => 'PluginProtypes_ActionRegistration',
  ),
  'template'=>array(
    'actions/ActionSettings/profile.tpl' => '../../../plugins/protypes/templates/skin/new/actions/ActionSettings/profile.tpl',
    'actions/ActionRegistration/index.tpl' => '../../../plugins/protypes/templates/skin/new/actions/ActionRegistration/index.tpl',
  )
);

На мой взгляд, всё это лучше бы выглядело, например, так:

protected $aDelegates=array(
  'action'  =>array(
    'ActionSettings' => '#__ActionSettings',
    'ActionRegistration' => '#__ActionRegistration',
  ),
  'template'=>array(
    'actions/ActionSettings/profile.tpl' => '#__actions/ActionSettings/profile.tpl',
    'actions/ActionRegistration/index.tpl' => '#__actions/ActionRegistration/index.tpl',
  )
);


3) Я не нашел возможности делегировать темплейты, которые подключаются динамически — из других темплейтов. Например, header_top.tpl, paging.tpl, topic_list.tpl. Этого очень не хватает, есть ли возможность что-то придумать для таких ситуаций?
avatar
3. Делегирования динамически подключаемых шаблонов нет. Для этого нужно лезть внутрь Smarty.
avatar
а что насчет (2)?
avatar
Интересная мысль! Только можно вообще без решетки, а просто с подчеркивания начинать. Т.е., если есть плагин MyPlugin и там делегирование описано так:
'ActionSettings' => '_ActionSettings'

то подразумевается, что полная форма записи такая:
'ActionSettings' => 'MyPlugin_ActionSettings'


Вот только плохо, что сам модуль Plugin нельзя делегировать :(
avatar
'ActionSettings' => '_ActionSettings'

Я только не пойму, зачем тогда вообще что-то указывать? =) Можно вспомнить Ruby On Rails и пойти по пути COC — если в качестве делегата ничего не указано — значит делегатов является одноименный ресурс из плагина, который производит делегирование.
avatar
Это конечно здорово, но можно предположить, что в случае с темплейтами имя файла может и не быть идентичным, а вот длинный относительный путь от одной папки с шаблонами до другой — не очень красиво :)
Думаю, стоит реализовать оба варианта.
avatar
COC как раз и предполагает два пути
avatar
С РоР не работал толком, поэтому для меня лично эта аналогия не катит. Если явно не указывать, то как? По имени самого плагина? Или перебирать экшены (модули, сущности), которые входят в состав плагина и сравнивать их с оригинальными компонентами?
avatar
просто, к примеру вот так:
'template'=>array(
    'actions/ActionSettings/profile.tpl',
    'actions/ActionRegistration/index.tpl',
  )

то есть будет подразумеваться, что неуказанный делегат лежит в папке /plugins/myplugin/templates/skin/myskin/.
avatar
Посмотри как я сделал это для модулей, экшенов и сущностей: в XML вместо

<delegate>
   <module>
      <item>
          <from>User</from>
          <to>PluginTest_User</to>
      </item>
   </module>
</delegate>

достаточно написать:
User



Фактически я указываю только название модуля, который хочу подменить и все. Рас я не указал явно делегат — значит считаем, что мой таргет имеет аналогичное с исходным расположение и название — добавляем в начало приставку и все.
avatar
А вы дайте доступ к объекту Смарти, глядишь, кто-то что-то и придумает ;)
avatar
Насчет (1): ошибка была из-за того, что Router::GetActionClass() возращает имя экшена без учета делегирования.
Возможное решение: добавить на 162-ой строчке файла /engine/classes/Router.class.php сохранение нового названия экшена, если учитывается делегирование:
$sActionClass=$this->Plugin_GetDelegate('action',$sActionClass);
self::$sActionClass = $sActionClass;

Создал тикет.
avatar
создал топик с остальными фиксами и предложениями:
avatar
3. теперь должен заработать
avatar
вот это отличная новость!
avatar
Проверил, работает, спасибо.
Еще бы сделали чтобы можно было делегировать темплейт блоков, например block.stream.tpl, отдельно его элементы (block.stream_comment.tpl) можно делегировать, но саму основу нет.
Как понимаю всего то надо в smarty_insert_block строчку
$sBlockTemplate = 'block.'.$aParams['block'].'.tpl';
заменить на
$sBlockTemplate = Engine::getInstance()->Plugin_GetDelegate('template','block.'.$aParams['block'].'.tpl');

у меня так заработало.
avatar
Теперь можно использовать короткую запись:
<template>
	<item>
		<from>actions/ActionProfile/whois.tpl</from>		
	</item>
	<item>
		<from>actions/ActionBlog/index.tpl</from>
		<to>_actions/ActionBlog/index_new.tpl</to>
	</item>
</template>
avatar
ещё одно замечание. в текущей реализации ссылку на ленту друзей видят все пользователи (и гости в том числе), но у гостей она ведь не работает.

поэтому я добавил у себя проверку в шаблоне при выводе кнопки
{if $oUserCurrent}
                                                <li {if $sMenuSubItemSelect=='friends'}class="active"{/if}>
                                                        <div><div><div>
                                                                <a href="{router page='friends'}">{$aLang.plugin_friends_menu_title}</a>{if $iCountTopicsFriendsNew} +{$iCountTopicsFriendsNew}{/if}
                                                        </div></div></div>
                                                </li>
                                                {/if}


и в экшене
/**
         * Инициализация
         *
         * @return void
         */
        public function Init() {
                // Закрываем страницу для неавторизированных пользователей
		if(!$this->User_IsAuthorization()) {
			return parent::EventNotFound();
		}


может я что не так делаю. поправьте тогда
avatar
и в инит плагина ещё добавил
$this->Viewer_Assign('oUserCurrent', $this->User_GetUserCurrent());
avatar
ещё одно замечание. в текущей реализации
Я знаю, что в этом плагине еще много чего можно доделать, он был написал для того, чтобы показать сам процесс создания плагина. А дальше — полет вашей фантазии и творчества.
avatar
я понимаю. но плагин получился полезный и я уже несколько недель использую его на cookorama.net
предлагаю вынести его в отдельный репозиторий и развивать дальше
avatar
Плагин буду куда-то выносить и развивать только после выхода v.0.4 =) Пока развиваю ядро.
avatar
Спасибо
avatar
большое спасибо за пример написания плагина

в экшене небольшая неточность:

<?php

class PluginFriends_ActionFeed extends ActionPlugin {
        /**
         * Главное меню
         *
         * @var unknown_type
         */
        protected $sMenuHeadItemSelect='blog';
        /**
         * Меню
         *
         * @var unknown_type
         */
        protected $sMenuItemSelect='index';


в результате сабменю выводится от индекса, надо:

protected $sMenuItemSelect='friends';
avatar
хорошо бы добавить вывод топиков только из тех блогов, на которые подписан. Это, мне кажется, куда важнее, чем топики друзей, т.к. человек подключился к 10-ти блогам, и потом может в отдельном разделе смотреть обновления их этих блогов.
avatar
AddMenu вместо добавления пункта в меню, заменяет его полностью на menu.blog.tpl.
$this->Viewer_AddMenu('blog',Plugin::GetTemplatePath(__CLASS__).'/menu.blog.tpl');

livestreet v0.4.1
avatar
А как ссылку на ленту друзей поместить в профиль пользователя? Т.е. имеется в виду, чтобы она была видна и доступна у каждого пользователя и ей могли пользоваться и другие пользователи сайта? Зашел к другу — увидел у него ленту — ознакомился — добавил себе заинтересовавших тебя авторов…
avatar
Интересная задумка… надо подумать…
avatar
будет ли этот плагин оформлен и выложен как готовый плагин?
avatar
плагин «Аудиозаписи» для 0.4.2
livestreet.ru/blog/4777.html
avatar
для версии 4.2 небольшая поправка… имя класса поправить PluginFriends_Friends ибо вылетает error'ка
Fatal error: Class 'PluginFriends_ModuleFriends' not found in X:\home\myblog\www\engine\classes\Engine.class.php on line 162
avatar
А что именно то нужно поправить, имена классов и т.п. я уже изменил с Friends на Fri… и всё-равно та же ошибка…
avatar
в файле plugins\friends\classes\modules\friends\Friends.class.php имя класса PluginFriends_Friends изменить на PluginFriends_ModuleFriends
avatar
netlanc, спасибо!
avatar
Спасибо автору! Только что реализовал у себя — вроде работает нормально (пока наполнения полноценного нету у сайта).

Очень полезная вещь. Странно, что такой функции у LS нету изначально. Хотя с другой стороны — хорошо, когда есть куда развиваться.

Еще раз спасибо!
  • UJey
  • 0
avatar
Товарищ, мы узнали что вам все же удалось реализовать написанное тут без багов, не могли бы вы поделится своей сборкой?
avatar
кто мы? и о каких багах идет речь?
avatar
Мы это множество людей которые ищут готовое решение. Я лично столкнулся с тем что некоторые плагины стали вылетать. Пришлось править код для 4.2 Но так ни чего и не получилось. Хотел попросить тебя выложить архивом, есле у тебя получилась эта задумка.
avatar
Готов выложить свой вариант кода (всего плагина). Но думаю правильно будет сделать следующим образом:
1. скажите мне какие баги встречаются
2. я проверю свой код (и проект) на наличие таковых, чтобы не выкладывать код, который не факт, что работает правильно.
3. исправляю баги, если такие будут найдены.
4. выкладываю здесь ссылку на архив.
avatar
Вы совершенно правы.
Могу сказать за себя, ставил все на 4.2
'plugin_friends_menu_title' => 'Лента друзей'

Не отображается в меню…
Пытался поставить в Профиль, но тоже какой-то косяк. Скорее всего кривые руки))… лечу их активным чтением документации.
avatar
Так если дело в этом — я вручную в шаблон меню (на главной) вставил ссылку на «ленту друзей».
avatar
Я поставил… не пашет.
В логе идет ошибка. Собрал криво плагин, от того и прошу его, чтоб разобраться что и куда не так засунул-то в итоге.
avatar
Вот ссылка на плагин в том виде, в котором он у меня работает.
www.ex.ua/view_storage/454673494809

Сразу скажу, что LS допиливал, поэтому не все может быть стандартно…

Попробуй.
avatar
Ща гляну… спасибо.
avatar
Пашет но глюканул весь нав хедер… и поиск куда-то исчез после активации.
avatar
Заменил пару строк в шаблоне под него и все запахало, как родное))
Спасибо
avatar
Рад, что смог помочь! ))
Я ж говорил — допиливал все под себя.
avatar
Все как часы пашет.
Еще раз спасибо=)
avatar
пробовали ли вы в хроме зайти в ленту?
Fatal error: Call to a member function getId()on a non-object in /home/mediciol/public_html/plugins/friendsfeed/classes/actions/ActionFeed.class.php on line 60
avatar
вот в хроме:
Fatal error: Call to a member function getId() on a non-object in /home/mediciol/public_html/plugins/friendsfeed/classes/actions/ActionFeed.class.php on line 60

В фоксе работает на ура, может в курсе в чем проблема?
avatar
это при заходе в саму ленту, все остальное работает
avatar
Дело не в броузере. Если ты не выполнил вход (логин), то такое сообщение будет отображаться, поскольку в строке 60 оно пытается получить ID твоего текущего пользователя.

Вот и получается, что в FF работает (где есть сессия), а в Хроме — нет.

Как вариант решения — прятать ссылку на ленту для анонимных пользователей или при переходе на ленту предлагать выполнить вход (перебрасывать на страницу логина).
avatar
Дело не в броузере. Если ты не выполнил вход (логин), то такое сообщение будет отображаться, поскольку в строке 60 оно пытается получить ID твоего текущего пользователя.

Вот и получается, что в FF работает (где есть сессия), а в Хроме — нет.

Как вариант решения — прятать ссылку на ленту для анонимных пользователей или при переходе на ленту предлагать выполнить вход (перебрасывать на страницу логина).

Например в этом же файле в функции Init вначале добавить такой код:

/**
* Проверяем авторизован ли юзер
*/
if (!$this->User_IsAuthorization()) {
	return Router::Action('login');
}
avatar
Конечно же это плюс!
avatar
А как сделать, что бы на всех страницах было «Лента друзей +2»
сейчас появляется только когда захожу на test.ru/friends/
avatar
а может кто-то выложить архивчиком уже доделаный под 0.4.2 плагин? спасибо)
  • romi
  • 0
avatar
Нужна помощь:
1) Адаптировать плагин по шаблон new
2) Чтобы ссылка на ленту друзей была видна из любой страницы
3) RSS ленты друзей — если это реально сделать
Готов заплатить
avatar
У вас ошибка в комментарии. Перед кодом plugin.xml название файла написано с большой буквы, когда должно быть с маленькой
avatar
Скажите, а для версии 0.5.1 актуален мануал?
avatar
Подскажите, я пишу плагин и хотел бы добавить несколько ивентов к уже существ экшену, допустим, blogs. Я задаю в конфиге своего будущего плагина:
Config::Set('router.page.blogs', 'Plugin[Name]_ActionBlogs');

Но тогда все весь первоночальный экшн перезаписывается моим (?) и вместо /blogs/ я получаю, что страница не найдена, хотя мои ивенты работают. Нужно ли делегир? добавлять в классы плагина, как было написано в комментах? Смотрел другие плагины, там вместо page.blogs стомт page.[PluginName]_blogs, но тогда у меня не работают ивенты плагина. Почему не пойму…
avatar
как подобно этому $this->Viewer_AddMenu('blog',Plugin::GetTemplatePath(__CLASS__).'/menu.blog.tpl');
avatar
сори

как подобно этому
$this>Viewer_AddMenu('blog',Plugin::GetTemplatePath(__CLASS__).'/menu.blog.tpl');
добавить в sidebar
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.