|
Поделиться

[BETA] extender: конструктор слушателей событий

Начиная с версии 3.1.x, моды были заменены "расширениями", которые выполняют ту же самую функцию, но без изменений в исходном коде движка.

Сообщение c61 » 13.03.15 11:56

Extender: core and template event listeners constructor
phpBB 3.1, 3.2 extension

copyright (c) 2014 c61 c61@yandex.ru http://c61.no-ip.org
license http://opensource.org/licenses/gpl-license.php GNU Public License


Extender: Конструктор слушателей событий ядра и шаблонов

Создано по мотивам расширения Empennage http://c61.no-ip.org/forum/viewtopic.php?f=11&t=108

Расширение позволяет добавлять слушателей событий ядра и шаблонов, в результате Вы можете конструировать собственные расширения без необходимости создания их файлов в соответствии с правилами phpBB.

В обработчике событий Вы можете добавлять код и контент на страницы (css, html, javascript) для всех стилей и в слушатели событий ядра, модифицировать любые переменные шаблонов, добавлять свои переменные и т.п., то есть создавать свои расширения. Это может быть использовано, например, для добавления объявлений, счетчиков, информеров, отладки собственных стилей и скриптов.

Слушатели событий ядра, использование:

<!-- EVENT [Priority] PHP_Event[, PHP_Event[, HP_Event]] -->
PHP-код
<!-- ENDEVENT -->
Где:
• Priority - приоритет (отрицательное число), чем больше по абсолютному знгачению число, тем меньше приоритет обработчика события;
• PHP_Event - идентификатор события ядра (см. phpBB Wiki: Event List Event List).

Любой текст вне блока EVENT рассматривается как комментарий и не обрабатывается.

Пример:
<!-- EVENT core.page_footer_after -->
$this->rootref['SITE_LOGO_IMG'] = '<img src="http://mysite.ru/forum/styles/prosilver/site-logo.gif" style="max-width: 100%; height:auto; width:auto;">';
<!-- ENDEVENT -->

Слушатели событий шаблонов, использование

<!-- EVENT [PRE] Template_Event -->
здесь какой-либо код
<!-- ENDEVENT -->
Где:
• [PRE] - префикс, указывающий на необходимость компиляции шаблона для события перед обработкой шаблона ядром; если префикс опущен, компиляция выполняется ядром phpBB;
• Template_Event - идентификатор события шаблона (см. phpBB Wiki: Event List: Template Events Event List: Template Events и phpBB Wiki: Event List: ACP Template Events Event List: ACP Template Events).

Любой текст вне блока EVENT рассматривается как комментарий и не обрабатывается.

Примеры использования:
<!-- EVENT PRE overall_header_stylesheets_after -->
здесь какой-либо код
<!-- ENDEVENT -->
<!-- EVENT overall_footer_body_after -->
здесь какой-либо код
<!-- ENDEVENT -->

Внутри блока EVENT используйте:
• для JavaScript <script type="text/javascript">...</script>
• для StyleSheet <style type="text/css">...</style>
• для PHP <!-- PHP --> PHP-код <!-- ENDPHP -->

В событиях с префиксом PRE допустимы все конструкции для шаблонов phpBB 3.0.13 (кроме включений файлов, например, INCLUDE и т.п.), код PHP выполняется безусловно.

В событиях без префикса PRE допустимы все конструкции для шаблонов phpBB 3.1 и 3.2, код PHP выполняется только при условии разрешения PHP в шаблонах.

Вне кода PHP можно использовать:
• любые языковые переменные из языковых файлов (например, {L_<STRINGNAME>}, где <STRINGNAME> — это имя переведённой строки); так, {L_WROTE} будет отображено как «wrote» или переведено в зависимости от выбранного пользователем языка
• любые переменные, определённые для основного шаблона, например, {SCRIPT_NAME}; для локальных переменных, определённых посредством DEFINE, используйте {$VARNAME}.


Можно включить выдачу отладочной информации при обнаружении ошибок (при наличии ошибок внизу страницы появится сообщение компилятора и листинг скомпилированного кода).

PHP в слушателях событий ядра и шаблонов с префиксом PRE

Вы можете использовать:
• $this->event[] для доступа к данным события
• $this->rootref[] для доступа к переменным общего шаблона
• $this->tpldata[] для доступа к переменным шаблона
• $this->userlang[] для доступа к языковым переменным
• и др., см. исходный код расширения.

Все локальные переменные, определённые в уже обработанных событиях, доступны в новых событиях.

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

При наличии блокировки и включенном режиме выдачи отладочной информации внизу экрана выдаётся листинг и сообщение о блокировке.

Если фатальная ошибка произошла вне кода расширения, вместо пустой страницы выдаётся информация об ошибке, расширение не блокируется.

Для справки - коды ошибок, безусловно (*) или при определённых условиях (**) являющихся фатальными (условия отличаются от представленных в руководствах по PHP, поскольку в расширении для исполнения кода используется функция eval() с дополнительным обрамлением):
• [E_ERROR = 1] (*) Фатальные ошибки времени выполнения. Это неустранимые средствами самого скрипта ошибки, такие как ошибка распределения памяти и т.п. Выполнение скрипта в таком случае прекращается.
• [E_PARSE = 4] (**) Ошибки на этапе компиляции. Должны генерироваться только парсером.
• [E_CORE_ERROR = 16] (*) Фатальные ошибки, которые происходят во время запуска РНР. Такие ошибки схожи с E_ERROR, за исключением того, что они генерируются ядром PHP.
• [E_COMPILE_ERROR = 64] (**) Фатальные ошибки на этапе компиляции. Такие ошибки схожи с E_ERROR, за исключением того, что они генерируются скриптовым движком Zend.
• [E_USER_ERROR = 256] (**) Сообщения об ошибках сгенерированные пользователем. Такие ошибки схожи с E_ERROR, за исключением того, что они генерируются в коде скрипта средствами функции PHP trigger_error().
• [E_RECOVERABLE_ERROR = 4096] (**) Фатальные ошибки с возможностью обработки. Такие ошибки указывают, что, вероятно, возникла опасная ситуация, но при этом, скриптовый движок остается в стабильном состоянии. Если такая ошибка не обрабатывается функцией, определенной пользователем для обработки ошибок (см. set_error_handler()), выполнение приложения прерывается, как происходит при ошибках E_ERROR. Начиная с PHP 5.2.0

Все сообщения о фатальных ошибках либо в листинге выдаются на английском языке в связи с возможными ошибками заголовка страницы.


Отличие от Empennage и прочих подобных расширений - возможность использования любых событий ядра/шаблонов, переменных, написания программы для своих шаблонов. Расширение не боится никаких фатальных ошибок в коде PHP (разумеется, при условии правильного конфигурирования PHP).


Дополнительные переменные основного шаблона

Extender создаёт переменные с префиксами 'S_EXTENDER_' затем идентификатором события шаблона (большими буквами) и суффиксом '_HASH', в которых содержится crc32 (hex) соответствующего исходного кода.

Создаётся также S_EXTENDER_COOKIENAME - имя cookie для использования в javascript.


Для разработчиков

Для подсветки всех событий шаблонов на всех страницах для любого пользователя (на страницу настройки этого расширения в панели администратора опция не распространяется) достаточно добавить в конец текста окна
Код: Выделить всё
<!-- ALLEVENTS -->
<!-- ENDALLEVENTS -->
Не забудьте удалить блок ALLEVENTS вместе с автоматически добавленным в него содержимым после завершения поиска нужного события.


Установка

Копировать содержимое каталога root в корневую папку конференции с сохранением структуры каталогов (расширения phpBB 3.1 и 3.2 располагаются в /ext).
Разрешить запись в каталоги /ext/c61/extender/adm/style/event, /ext/c61/extender/styles/all/template/event.
Разрешить запись файлов /ext/c61/extender/adm/style/event/acp_overall_footer_after.html, /ext/c61/extender/styles/all/template/event/overall_footer_body_after.html.


Особые условия

В настройках PHP требуется 'register_globals = off' (по умолчанию начиная с PHP v.5.3).


Примеры:

Свой логотип

Core:
Код: Выделить всё
<!-- EVENT core.page_footer_after -->
$this->rootref['SITE_LOGO_IMG'] = '<img src="http://mysite.ru/forum/styles/prosilver/site-logo.gif" style="max-width: 100%; height:auto; width:auto;">'
<!-- ENDEVENT -->
Template:нет

Объявление для гостей (боты и зарегистрированные пользователя игнорируются)

Core:нет
Template:
Код: Выделить всё
<!-- EVENT PRE  overall_header_content_before -->
<!-- IF not S_IS_BOT -->
<!-- IF not S_REGISTERED_USER -->
<div id="announcement" style="display:none;"></div>
<script type="text/javascript">
// <![CDATA[
function EXsetCookie(hide) {
	var date = new Date;
	date.setDate(date.getDate() + 1000);
	document.cookie = "{S_EXTENDER_COOKIENAME}=" + hide + "; path=/; expires=" + date.toUTCString();
}
function EXgetCookie()
{
	var eqname = "{S_EXTENDER_COOKIENAME}=";
	var cookies = document.cookie.split(";");
	for ( var i = 0; i < cookies.length; i++ ) {
		var cookie = cookies[i];
		while ( cookie.charAt(0) == " " ) cookie = cookie.substring(1, cookie.length);
		if ( !cookie.indexOf(eqname) ) {
			var d = parseInt(cookie.substring(eqname.length, cookie.length));
			return d;
		}
	}
	return 0;
}
var announcement = document.getElementById("announcement");
if ( announcement ) {
	var announcement_title="{L_VIEW_TOPIC_ANNOUNCEMENT}";
	var announcement_text="Текст объявления";
	if ( EXgetCookie() != {S_EXTENDER_OVERALL_HEADER_CONTENT_BEFORE_HASH} ) {
		announcement.style.display="block";
	}
	announcement.innerHTML='<font color="darkred" size="+0.5em"><div><input type="button" value="&nbsp;&times;&nbsp;" style="background: transparent; border: 1px solid lightgray; margin: 0; padding: 0; font-weight: bolder; vertical-align: top;" onclick="announcement.style.display='+"'"+'none'+"'"+'; EXsetCookie({S_EXTENDER_OVERALL_HEADER_CONTENT_BEFORE_HASH});" />&nbsp;<b style="vertical-align: bottom;">'+announcement_title+'</b> </div> <div> <div style="display: block;">'+announcement_text+'<hr></hr></div></div></font><br />';
}
// ]]>
</script>
<!-- ENDIF -->
<!-- ENDIF -->
<!-- ENDEVENT -->


Установить иконку сайта (например, не favicon.ico в корне)

Core:нет
Template:
Код: Выделить всё
// favicon для конференции
<!-- EVENT overall_header_head_append -->
<link rel='icon' href='/myfavicon.png' type='image/x-icon'>
<link rel='shortcut icon' href='/myfavicon.png' type='image/x-icon'>
<!-- ENDEVENT -->
// favicon для админки
<!-- EVENT acp_overall_header_head_append -->
<link rel='icon' href='/myfavicon.png' type='image/x-icon'>
<link rel='shortcut icon' href='/myfavicon.png' type='image/x-icon'>
<!-- ENDEVENT -->


Установка счётчиков и информеров на страницы конференции

На примере Яндекса; замените XXXXXXXX на свой идентификатор.

Core:нет
Template:
Код: Выделить всё
<!-- EVENT overall_footer_copyright_append -->
<!-- Yandex.Metrika counter -->
<script type="text/javascript">
(function (d, w, c) {
    (w[c] = w[c] || []).push(function() {
        try {
            w.yaCounterXXXXXXXX = new Ya.Metrika({id:XXXXXXXX,
                    clickmap:true,
                    trackLinks:true,
                    accurateTrackBounce:true});
        } catch(e) { }
    });

    var n = d.getElementsByTagName("script")[0],
        s = d.createElement("script"),
        f = function () { n.parentNode.insertBefore(s, n); };
    s.type = "text/javascript";
    s.async = true;
    s.src = (d.location.protocol == "https:" ? "https:" : "http:") + "//mc.yandex.ru/metrika/watch.js";

    if (w.opera == "[object Opera]") {
        d.addEventListener("DOMContentLoaded", f, false);
    } else { f(); }
})(document, window, "yandex_metrika_callbacks");
</script>
<!-- <noscript><div><img src="//mc.yandex.ru/watch/XXXXXXXX" style="position:absolute; left:-9999px;" alt="" /></div></noscript> -->
<!-- /Yandex.Metrika counter -->
<!-- ENDEVENT -->
<!-- EVENT overall_footer_after -->
<div width="100%" align="center">
<!-- Yandex.Metrika informer -->
<a href="http://metrika.yandex.ru/stat/?id=XXXXXXXX&amp;from=informer"
target="_blank" rel="nofollow"><img src="//bs.yandex.ru/informer/XXXXXXXX/3_0_373738FF_171718FF_1_pageviews"
style="width:88px; height:31px; border:0;" alt="Яндекс.Метрика" title="Яндекс.Метрика: данные за сегодня (просмотры, визиты и уникальные посетители)" onclick="try{Ya.Metrika.informer({i:this,id:XXXXXXXX,lang:\'ru\'});return false}catch(e){}"/></a>
<!-- /Yandex.Metrika informer -->
</div>
<!-- ENDEVENT -->


Установка своих языковых переменных

Core:
Код: Выделить всё
<!-- EVENT core.user_setup -->
switch ($this->event['user_lang_name']) {
	case 'ru': {
		$this->userlang += array(
			'MYLANGVAR1'	=> 'Моя первая языковая переменная',
			'MYLANGVAR2'	=> 'Моя вторая языковая переменная',
		);
		break;
	}
	default: {
		$this->userlang += array(
			'MYLANGVAR1'	=> 'My first language var',
			'MYLANGVAR2'	=> 'My second language var',
		);
		break;
	}
}
<!-- ENDEVENT -->
Template:
Код: Выделить всё
// Проверка установки языковых переменных
<!-- EVENT overall_footer_copyright_append -->
{L_MYLANGVAR1} {L_MYLANGVAR2}
<!-- ENDEVENT -->


Удаление Re: в заголовках тем и дубликатов subject в темах (аналог расширения c61\postsubject)

Core:
Код: Выделить всё
<!-- EVENT core.page_footer_after -->
if ( (($this->rootref['SCRIPT_NAME'] == 'viewtopic')
  || ($this->rootref['SCRIPT_NAME'] == 'posting')
     )
 && isset($this->rootref['TOPIC_TITLE']) ) {
	$tt = $this->rootref['TOPIC_TITLE'];
	$indexes = array('postrow','topic_review_row');
	foreach ( $indexes as $index ) {
		if ( !isset($this->tpldata[$index]) ) continue;
		foreach ( $this->tpldata[$index] as $key => $val ) {
			if ( isset($this->tpldata[$index][$key]['POST_SUBJECT']) ) {
				$ps = $this->tpldata[$index][$key]['POST_SUBJECT'];
				if ( !empty($ps) ) {
					if ( ($ps == $tt)
					  || ($ps == 'Re: '.$tt)
					  || ($ps == 'Re:'.$tt)
					  || ($ps == 'Re: '.substr($tt,0,-4))
					  || ($ps == 'Re:'.substr($tt,0,-3))
					   ) $ps = $this->tpldata[$index][$key]['POST_SUBJECT'] = '';
					else if ( preg_match('/^Re:\s*(.*)/isu',$ps,$matches) ) $ps = $this->tpldata[$index][$key]['POST_SUBJECT'] = $matches[1];
				}
			}
		}
	}
	if ( isset($this->rootref['L_POST_SUBJECT']) ) {
		$this->rootref['L_POST_SUBJECT'] = '<span class="postsubject_remove">&nbsp;</span>';
	}
}

<!-- ENDEVENT -->
<!-- EVENT core.display_forums_modify_template_vars -->
$forum_row = $this->event['forum_row'];
$modified = false;
if ( isset($forum_row['LAST_POST_SUBJECT']) ) {
	if ( preg_match('/^Re:\s*(.*)/isu',$forum_row['LAST_POST_SUBJECT'],$matches) ) {
		$forum_row['LAST_POST_SUBJECT'] = $matches[1];
		$modified = true;
	}
}
if ( isset($forum_row['LAST_POST_SUBJECT_TRUNCATED']) ) {
	if ( preg_match('/^Re:\s*(.*)/isu',$forum_row['LAST_POST_SUBJECT_TRUNCATED'],$matches) ) {
		$forum_row['LAST_POST_SUBJECT_TRUNCATED'] = $matches[1];
		$modified = true;
	}
}
if ( $modified ) $this->event['forum_row'] = $forum_row;
<!-- ENDEVENT -->
<!-- EVENT core.viewforum_modify_topicrow -->
$topic_row = $this->event['topic_row'];
$modified = false;
if ( isset($topic_row['LAST_POST_SUBJECT']) ) {
	if ( preg_match('/^Re:\s*(.*)/isu',$topic_row['LAST_POST_SUBJECT'],$matches) ) {
		$topic_row['LAST_POST_SUBJECT'] = $matches[1];
		$modified = true;
	}
}
if ( $modified ) $this->event['topic_row'] = $topic_row;
<!-- ENDEVENT -->
Template:
Код: Выделить всё
<!-- EVENT overall_header_head_append -->
<style type="text/css">
span.postsubject_remove {
	padding: 0px;
	margin: 0px;
	visibility: hidden;
}
</style>
<!-- ENDEVENT -->
<!-- EVENT overall_footer_after -->
<script type="text/javascript">
// <![CDATA[
function PShide() {
	var ps = document.getElementsByClassName("postsubject_remove");
	for ( var i=0; i<ps.length; i++ ) {
		var p = ps[i].parentNode;
		if ( p ) {
			p.style.display = "none";
		}
	}
}
PShide();
// ]]>
</script>
<!-- ENDEVENT -->


Расширение "Detailed viewonline by rxu" (rxu/DetailedViewonline)

Core:
Код: Выделить всё
<!-- EVENT core.user_setup -->
switch ($this->event['user_lang_name'])
{
	case 'ru':
	{
		$this->userlang['READING_THE_TOPIC']		= 'Просматривает тему <strong>%1$s</strong> в форуме %2$s';
		$this->userlang['READING_THE_TOPIC_PAGE']	= 'Просматривает страницу %3$s темы <strong>%1$s</strong> в форуме %2$s';
		$this->userlang['READING_THE_NEW_POSTS']	= 'Просматривает непрочитанные сообщения темы <strong>%1$s</strong> в форуме %2$s';
		$this->userlang['READING_THE_POST']			= 'Просматривает сообщение темы <strong>%1$s</strong> в форуме %2$s';
		break;
	}
	default:
	{
		$this->userlang['READING_THE_TOPIC']		= 'Reading the topic <strong>%1$s</strong> in %2$s';
		$this->userlang['READING_THE_TOPIC_PAGE']	= 'Reading page %3$s of the topic <strong>%1$s</strong> in %2$s';
		$this->userlang['READING_THE_NEW_POSTS']	= 'Reading new posts of the topic <strong>%1$s</strong> in %2$s';
		$this->userlang['READING_THE_POST']			= 'Reading the post in the topic <strong>%1$s</strong> in %2$s';
		break;
	}
}
<!-- ENDEVENT -->
<!-- EVENT core.viewonline_overwrite_location -->
$forum_data = $this->event['forum_data'];
$on_page = $this->event['on_page'];
$location = $this->event['location'];
$location_url = $this->event['location_url'];
$row = $this->event['row'];

switch ($on_page[1])
{
	case 'viewtopic':

		preg_match('#[\?&]t=([0-9]+)#i', $row['session_page'], $topic_id);
		$topic_id = (sizeof($topic_id)) ? (int) $topic_id[1] : 0;

		preg_match('#[\?&]p=([0-9]+)#i', $row['session_page'], $post_id);
		$post_id = (sizeof($post_id)) ? (int) $post_id[1] : 0;

		preg_match('#[\?&]start=([0-9]+)#i', $row['session_page'], $start);
		$start = (sizeof($start)) ? (int) $start[1] : 0;
		$page = ($start) ? ($start / $this->config['posts_per_page']) + 1 : 0;

		$view = (preg_match('#[\?&]view=unread#i', $row['session_page'])) ? true : false;

		if ($post_id)
		{
			$sql_ary = array(
				'SELECT'	=> 't.topic_id, t.topic_title, t.forum_id',
				'FROM'		=> array(
					POSTS_TABLE		=> 'p',
					TOPICS_TABLE	=> 't',
				),
				'WHERE'		=> 't.topic_id = p.topic_id
					AND p.post_id = ' . $post_id,
			);
		}
		else
		{
			$sql_ary = array(
				'SELECT'	=> 't.topic_title, t.forum_id',
				'FROM'		=> array(
					TOPICS_TABLE	=> 't',
				),
				'WHERE'		=> 't.topic_id = ' . $topic_id,
			);
		}

		$result = $this->db->sql_query($this->db->sql_build_query('SELECT', $sql_ary));
		if ($topicdata = $this->db->sql_fetchrow($result))
		{
			$topic_id = ($topic_id) ? : (int) $topicdata['topic_id'];
			$forum_id = (int) $topicdata['forum_id'];
			if ($forum_id && $this->auth->acl_get('f_list', $forum_id))
			{
				$topic_title = $topicdata['topic_title'];
				if ($post_id)
				{
					$location = sprintf($this->userlang['READING_THE_POST'], $topic_title, $forum_data[$forum_id]['forum_name']);
					$location_url = append_sid("{$this->phpbb_root_path}viewtopic.$this->php_ext", "p=$post_id#p$post_id");
				}
				else if ($start)
				{
					$location = sprintf($this->userlang['READING_THE_TOPIC_PAGE'], $topic_title, $forum_data[$forum_id]['forum_name'], $page);
					$location_url = append_sid("{$this->phpbb_root_path}viewtopic.$this->php_ext", 'f=' . $forum_id . '&amp;t=' . $topic_id . '&amp;start=' . $start);
				}
				else if ($view)
				{
					$location = sprintf($this->userlang['READING_THE_NEW_POSTS'], $topic_title, $forum_data[$forum_id]['forum_name']);
					$location_url = append_sid("{$this->phpbb_root_path}viewtopic.$this->php_ext", 'f=' . $forum_id . '&amp;t=' . $topic_id . '&amp;view=unread#unread');
				}
				else
				{
					$location = sprintf($this->userlang['READING_THE_TOPIC'], $topic_title, $forum_data[$forum_id]['forum_name']);
					$location_url = append_sid("{$this->phpbb_root_path}viewtopic.$this->php_ext", 'f=' . $forum_id . '&amp;t=' . $topic_id);
				}
			}
		}
		$this->db->sql_freeresult($result);
	break;

	case 'search';
		preg_match('#search_id=([a-z_]+)#i', $row['session_page'], $search_id);
		$search_id = (!empty($search_id[1])) ? $search_id[1] : '';
		$search_mode = array('egosearch' => 'SEARCH_SELF', 'unanswered' => 'SEARCH_UNANSWERED', 'unreadposts' => 'SEARCH_UNREAD', 'newposts' => 'SEARCH_NEW', 'active_topics' => 'SEARCH_ACTIVE_TOPICS');
		$location = $this->userlang['SEARCHING_FORUMS'] . (($search_id) ? ': <strong>' . $this->userlang[$search_mode[$search_id]] . '</strong>' : '');
		$location_url = append_sid("{$this->phpbb_root_path}search.$this->php_ext", ($search_id) ? 'search_id=' . $search_id : '');
	break;

	case 'memberlist';
		preg_match('#[\?&]u=([0-9]+)#i', $row['session_page'], $user_id);
		$user_id = (sizeof($user_id)) ? (int) $user_id[1] : 0;
		if ($user_id)
		{
			$sql = 'SELECT username, user_colour FROM ' . USERS_TABLE . '
				WHERE user_id = ' . $user_id;

			$result = $this->db->sql_query($sql);
			if ($userdata = $this->db->sql_fetchrow($result))
			{
				$username = get_username_string('no_profile', $user_id, $userdata['username'], $userdata['user_colour'], $userdata['username']);
				$location = $this->userlang['VIEWING_MEMBER_PROFILE'] . ' <strong>' . $username;
				$location_url = append_sid("{$this->phpbb_root_path}memberlist.$this->php_ext", "mode=viewprofile&amp;u=$user_id");
			}
			$this->db->sql_freeresult($result);
		}
	break;

	case 'download/file':
		preg_match('#[\?&]id=([0-9]+)#i', $row['session_page'], $file_id);
		$file_id = (sizeof($file_id)) ? (int) $file_id[1] : 0;
		if ($file_id)
		{
			$sql = 'SELECT real_filename, post_msg_id FROM ' . ATTACHMENTS_TABLE . '
				WHERE attach_id = ' . $file_id;

			$result = $this->db->sql_query($sql);
			if ($filedata = $this->db->sql_fetchrow($result))
			{
				$location = $this->userlang['DOWNLOADING_FILE'] . ' <strong>' . $filedata['real_filename'] . '</strong>';
				$location_url = append_sid("{$this->phpbb_root_path}viewtopic.$this->php_ext", "p={$filedata['post_msg_id']}#p{$filedata['post_msg_id']}");
			}
			$this->db->sql_freeresult($result);
		}
	break;

	case 'feed';
		$location = $this->userlang['FEED'];
		$location_url = append_sid("{$this->phpbb_root_path}feed.$this->php_ext");
	break;

	default:
	break;
}

$this->event['location']= $location;
$this->event['location_url'] = $location_url;
<!-- ENDEVENT -->
Template:нет

Минипрофили слева в viewtopic для prosilver

Core:
Код: Выделить всё
<!-- EVENT core.page_header_after -->
if ( ($this->rootref['SCRIPT_NAME'] == 'viewtopic') && ($this->rootref['T_THEME_NAME'] == 'prosilver') ) {
	preg_match('/MSIE (.*?);/', $this->rootref['S_USER_BROWSER'], $matches);
	if ( count($matches) < 2 ) preg_match('/Trident\/\d{1,2}.\d{1,2}; rv:([0-9]*)/', $this->rootref['S_USER_BROWSER'], $matches);
	if ( !( (count($matches) > 1) && ($matches[1] < 9) ) ) $this->rootref['S_EXTENDER_MINIPROFILES'] = true;
}
<!-- ENDEVENT -->
Template:
Код: Выделить всё
<!-- EVENT overall_header_head_append -->
<!-- IF S_EXTENDER_MINIPROFILES -->
<style type="text/css">
.online {
	background-position: 0% 0;
	background-image: none;
	position: relative;
	overflow: hidden;
}
.online:before {
	position: absolute;
	content: url("{T_THEME_PATH}/{S_USER_LANG}/icon_user_online.gif");
	top: 0px;
	left: 0px;
	-webkit-transform: rotate(-90deg);
	-moz-transform: rotate(-90deg);
	-ms-transform: rotate(-90deg);
	-o-transform: rotate(-90deg);
	transform: rotate(-90deg);
}
.postprofile {
	border-width: 0 1px 0 0;
	float: left;
	text-align: right;
	padding-right: 10px;
}
.postprofile .avatar {
	float: right;
}
.postbody {
	float: right;
}
@media only screen and (max-width: 700px), only screen and (max-device-width: 700px)
{
	.online {
		position: relative;
		background-image: url("{T_THEME_PATH}/{T_THEME_LANG_NAME}/icon_user_online.gif");
		background-position: 100% 0;
		background-repeat: no-repeat;
	}
	.online:before {
		position: absolute;
		content: "";
		top: 0px;
		left: 0px;
	}
	.postprofile {
		border-width: 0 0 0 1px;
		float: none;
		text-align: left;
		padding-right: 0;
	}
	.postprofile .avatar {
		float: left;
	}
	.postbody {
		float: left;
	}
}
</style>
<!-- ENDIF -->
<!-- ENDEVENT -->


Список пользователей online на каждой странице

Core:
Код: Выделить всё
<!-- EVENT core.page_header -->
if ( !$this->event['display_online_list'] || $this->event['item_id'] ) {
	$this->event['display_online_list'] = true;
	$this->rootref['S_ALL_PAGES_ONLINE_LIST'] = 1;
	if ( $this->event['item_id'] ) {
		if ( !function_exists('obtain_users_online') ) {
			require($this->phpbb_root_path . 'includes/functions.' . $this->php_ext);
		}
		$online_users = obtain_users_online(0, '');
		$user_online_strings = obtain_users_online_string($online_users, 0, '');
		$this->rootref['S_ALL_PAGES_ONLINE_USERS_LIST'] = $user_online_strings['online_userlist'];
	}
}
<!-- ENDEVENT -->
<!-- EVENT core.page_header_after -->
if ( isset($this->rootref['S_ALL_PAGES_ONLINE_LIST']) && $this->rootref['S_ALL_PAGES_ONLINE_LIST'] ) {
	unset($this->rootref['S_DISPLAY_ONLINE_LIST']);
}
<!-- ENDEVENT -->
Template:
Код: Выделить всё
<!-- EVENT overall_footer_content_after -->
<!-- IF S_ALL_PAGES_ONLINE_LIST -->
<!-- IF SCRIPT_NAME ne 'viewonline' -->
	<div class="stat-block online-list">
		<!-- IF U_VIEWONLINE --><h3><a href="{U_VIEWONLINE}">{L_WHO_IS_ONLINE}</a></h3><!-- ELSE --><h3>{L_WHO_IS_ONLINE}</h3><!-- ENDIF -->
		<p>
			{TOTAL_USERS_ONLINE} ({L_ONLINE_EXPLAIN})<br />{RECORD_USERS}<br /> <br />
<!-- IF S_ALL_PAGES_ONLINE_USERS_LIST -->
{S_ALL_PAGES_ONLINE_USERS_LIST}<br/> <br />
<!-- ENDIF -->
{LOGGED_IN_USER_LIST}
		</p>
	</div>
<!-- ENDIF -->
<!-- ENDIF -->
<!-- ENDEVENT -->


Добавление навигационных ссылок

Core:Нет
Template:
Код: Выделить всё
<!-- EVENT navbar_header_quick_links_before -->
<li class="small-icon icon-search-self"><a href="http://mysite.ru" target="_blank" role="menuitem">Мой сайт</a></li> 
<li class="small-icon icon-search-self"><a href="http://old.mysite.ru" target="_blank" role="menuitem">Мой старый сайт</a></li> 
<li class="separator"></li> 
<!-- ENDEVENT -->


Добавление ссылки на ленту новостей

Core:Нет
Template:
Код: Выделить всё
<!-- EVENT overall_header_navigation_append  -->
<!-- IF T_THEME_NAME == 'prosilver' -->
<li class="small-icon icon-feed data-last-responsive="true"><a href="./feed.php" title="Atom Feed" role="menuitem">Лента новостей</a></li>
<!-- ENDIF -->
<!-- ENDEVENT -->


Добавление ссылки на ленту новостей в стилях от Artodia

Core:Нет
Template:
Код: Выделить всё
<!-- EVENT overall_header_stylesheets_after -->
<!-- IF T_THEME_NAME != 'prosilver' -->
<style type="text/css">
.icon-rss {
	position:relative;
	background-image:none;
}
.icon-rss:after {
	font-family: 'Glyphicons Regular','Glyphicons';
	content: '\E074';	// glyphicons
	width: 18px;
	text-align: center;
	color:#3462a0;
	position:absolute;
	top:50%;
	left:0;
	height:14px;
	margin-top:-7px;
	text-align:center;
	font-size:12px;
	line-height:14px;
	vertical-align:baseline;
	font-weight:normal;
	font-style:normal;
	text-transform:none;
	text-indent:0;
	pointer-events:none;
	-moz-transform: rotate(45deg);
	-o-transform: rotate(45deg);
	-webkit-transform: rotate(45deg);
	-ms-transform: rotate(45deg);
	transform: rotate(45deg);
}
.icon-rss:hover:after {
	color:#b93329;
}
</style>
<!-- ENDIF -->
<!-- ENDEVENT -->
<!-- EVENT secondary_navlinks_after -->
<!-- IF T_THEME_NAME != 'prosilver' -->
<li class="small-icon icon-rss responsive-cloned-item"><a href="./feed.php" title="Atom Feed">Лента новостей</a></li>
<!-- ENDIF -->
<!-- ENDEVENT -->


Подключение автоматической подсветки синтаксиса для ББ-кода code

Core:Нет
Template:
Код: Выделить всё
<!-- EVENT overall_header_stylesheets_after -->
<!-- IF (SCRIPT_NAME eq 'viewtopic') or (SCRIPT_NAME eq 'posting') -->
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.7/styles/default.min.css">
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.7/highlight.min.js"></script>
<!-- ENDIF -->
<!-- ENDEVENT -->
<!-- EVENT overall_footer_body_after -->
<!-- IF (SCRIPT_NAME eq 'viewtopic') or (SCRIPT_NAME eq 'posting') -->
<script type="text/javascript">
$('code').each(function(i, block) {
	hljs.highlightBlock(block);
});
</script>
<!-- ENDIF -->
<!-- ENDEVENT -->


Изменение/упрощение prosilver

Core:Нет
Template:
Код: Выделить всё
<!-- EVENT overall_header_stylesheets_after -->
<!-- IF T_THEME_NAME == 'prosilver' -->
<style type="text/css">
/* width & positions */
body {
	padding: 0;
}
#wrap {
	border-radius: 0;
	max-width: 32768px;
	min-width: 625px;
	padding: 0 15px;
}
@media only screen and (max-width: 1220px), only screen and (max-device-width: 1220px) {
	#wrap {
		margin: 0 auto;
	}
}
#page-body {
	max-width: 1220px;
	margin: 4px auto;
}
#page-header, #page-footer /*, .forabg, .forumbg, .post, .panel */{
	margin-right: -15px;
	margin-left: -15px;
}
@media only screen and (max-width: 700px), only screen and (max-device-width: 700px) {
	#page-header, #page-footer, .forabg, .forumbg, .post, .panel {
		margin-right: 0;
		margin-left: 0;
	}
}
/* no radiuses */
.headerbar {
	margin-bottom: 0;
	border-radius: 0;
}
.navbar {
	border-radius: 0;
}
.forabg {
	border-radius: 0;
}
.forumbg {
	border-radius: 0;
}
.panel {
	border-radius: 0;
}
.post {
	border-radius: 0;
}
.dropdown {
	border-radius: 0;
}
.dropdown .dropdown-contents {
	border-radius: 0;
}
.pagination li a, .pagination li span {
	border-radius: 0;
}
#loading_indicator {
	border-radius: 0;
}
.dropdown-extended .header {
	border-radius: 0;
}
.postbody .content::-webkit-scrollbar, #topicreview::-webkit-scrollbar, #post_details::-webkit-scrollbar, .codebox code::-webkit-scrollbar, .attachbox dd::-webkit-scrollbar, .attach-image::-webkit-scrollbar, .dropdown-extended ul::-webkit-scrollbar {
	border-radius: 0;
}
.postbody .content::-webkit-scrollbar-thumb, #topicreview::-webkit-scrollbar-thumb, #post_details::-webkit-scrollbar-thumb, .codebox code::-webkit-scrollbar-thumb, .attachbox dd::-webkit-scrollbar-thumb, .attach-image::-webkit-scrollbar-thumb, .dropdown-extended ul::-webkit-scrollbar-thumb {
	border-radius: 0;
}
.rtl .search-box .inputbox {
	border-radius: 0;
}
.rtl .search-box a.button {
	border-radius: 0;
}
.button {
	border-radius: 0;
}
#tabs .tab > a {
	border-radius: 0;
}
#minitabs .tab > a {
	border-radius: 0;
}
.cp-mini {
	border-radius: 0;
}
@media only screen and (max-width: 900px), only screen and (max-device-width: 900px)
{
	#navigation li:first-child a {
		border-top-left-radius: 0;
		border-top-right-radius: 0;
	}
	#navigation li:first-child a {
		border-top-left-radius: 0;
		border-top-right-radius: 0;
	}
}
.search-box .inputbox {
	border-radius: 0;
}
.search-box button.search-icon {
	border-radius: 0;
}
.search-box a.button {
	border-radius: 0;
}
.search-header {
	border-radius: 0;
}
/* colors */
.forabg {
	background-color: #ffffff;
	background-image: none;
}
.forumbg {
	border-top: 1px solid #eff5f9;
	border-bottom: 1px solid #eff5f9;
	background-color: #ffffff;
	background-image: none;
}
.forumbg .header a, .forabg .header a, th a {
	color: #000000;
}
.forumbg .header a:hover, .forabg .header a:hover, th a:hover {
	color: #D31141;
}
li.header dt, li.header dd {
	color: #000000;
}
li.row {
	border-top-color:  #FFFFFF;
	border-bottom-color: #ffffff;
}
/* no uppercase */
h3 {
	text-transform: none;
	font-size: 125%;
}
table.table1 thead th {
	text-transform: none;
}
.dropdown-extended .header {
	text-transform: none;
}
li.header dt, li.header dd {
	text-transform: none;
	font-size: 125%;
}
.codebox p {
	text-transform: none;
}
.attachbox dt {
	text-transform: none;
}
</style>
<!-- ENDIF -->
<!-- ENDEVENT -->


Тест локальных переменных

Core:
Код: Выделить всё
<!-- EVENT core.common -->
$myvar = 1;
<!-- ENDEVENT -->
<!-- EVENT core.page_footer_after -->
++$myvar;
<!-- ENDEVENT -->
Template:
Код: Выделить всё
<!-- EVENT PRE overall_footer_body_after -->
<!-- PHP -->
echo '$myvar = '.$myvar;
<!-- ENDPHP -->
<!-- ENDEVENT -->


Тест обработки ошибок PHP (см. http://habrahabr.ru/post/161483/)

Для тестрования снимите комментарий с ошибочного оператора.

Core:
Код: Выделить всё
<!-- EVENT core.user_setup -->
// NONFATAL - E_NOTICE
// echo $undefined_var;

// NONFATAL - E_WARNING
// array_key_exists("key", NULL);

// NONFATAL - E_DEPRECATED
// split("[/.-]", "12/21/2012"); // split() deprecated начиная с php 5.3.0

// NONFATAL - E_STRICT
// class c {function f(){}} c::f();

// NONFATAL - E_USER_DEPRECATED
// trigger_error("E_USER_DEPRECATED", E_USER_DEPRECATED);

// NONFATAL - E_USER_WARNING
// trigger_error("E_USER_WARNING", E_USER_WARNING);

// NONFATAL - E_USER_NOTICE
// trigger_error("E_USER_NOTICE", E_USER_NOTICE);

// FATAL, если не обработана функцией set_error_handler - E_RECOVERABLE_ERROR
// class b {function f(int $a){}} $b = new b; $b->f(NULL);

// FATAL, если не обработана функцией set_error_handler - E_USER_ERROR
// trigger_error("E_USER_ERROR", E_USER_ERROR);

// FATAL - E_ERROR
// undefined_function();

// FATAL - E_PARSE
// parse_error

// FATAL - E_COMPILE_ERROR
// $var[];
// require "missing_file.php";
<!-- ENDEVENT -->
Template:нет


История версий

0.3.2 - 20160201:
- по просьбам трудящихся: корректная миграция с любой версии, в предыдущей была только с 0.3.0

0.3.1 - 20160201:
- найдена и устранена ещё одна ошибка, приводящая к конфликту с расширением "Быстрый ответ" и др., использующими AJAX
- победив лень, внял замечаниям Sumanai и сделал-таки нормальную миграцию ))

0.3.0 - 20160127:
- реализована совместимость расширения с PHP 7 и phpBB 3.2
- добавлены проверки разрешения записи в необходимые для работы расширения файлы

0.2.0 - 20160126:
- устранены конфликты с обновленным twig в phpBB 3.1.6, приводящие к конфликтам с расширением "Быстрый ответ" и др., использующими AJAX
- добавлены проверки разрешения записи в необходимые для работы расширения каталоги

0.1.9 - 20150923 (не распространялась):
- устранена ошибка формирования шаблонов при их отключении

0.1.8 - 20150905:
- CodeMirror работает теперь только в полноэкранном режиме (из-за проблем совместимости со стилями phpbb), для чего скрипт добавляет кнопку над окном редактирования кода
- в полноэкранном режиме CodeMirror слева вверху добавлена кнопка выхода (x), дублирующая F11 и Esc
- добавлено задание пути к CodeMirror
- для восстановления скрипта запуска CodeMirror достаточно отправить пустой текст кода в окне скрипта
- скрипт запуска CodeMirror несовместим с предыдущей версией, после установки новой версии расширения воспользуйтесь восстановлением скрипта

0.1.7 - 20150904:
- для редактирования кода приделан CodeMirror
- множество косметических изменений

0.1.6 - 20150903:
- теперь extender дополнительно устанавливает отдельную ловушку фатальных ошибок для других расширений и ядра phpbb
- оптимизированы по времени исполнения алгоритмы обработки событий ядра
- все задаваемые слушатели событий ядра выделены в отдельный код (ранее приоритет в первом описании относился ко всем заданным слушателем с таким именем)

0.1.5 - 20150829:
- устранена ошибка с приоритетами событий ядра, исправлено описание
- сделано корректное восстановление после set_error_handler - restore_error_handler
- добавлены примеры

0.1.4 - 20150331:
- устранена ошибка неопределённой переменной $eventBame, диагностируемая при включенном DEBUG

0.1.3 - 20150331:
- дополнены примеры
- устранена мелкая ошибочка set_error_handler если он не был установлен кодом phpBB

0.1.2 - 20150313:
- добавлена подсветка событий шаблонов
- дополнены примеры
- устранено множество мелких ошибок...

0.1.1 - 20150310:
- восстановил потерявшуюся переменную S_EXTENDER_COOKIENAME
- устранены ошибки обработки Template Events
- дополнительный контроль корректности идентификаторов событий
- добавлена обработка ошибок и предупреждений
- информация о фатальных ошибках добавлена в лог ошибок
- дополнены примеры
- добавлен режим разработчика: подсветка событий шаблонов на страницах конференции

0.1.0 - 20150309:
- реализовано по мотивам Empennage...

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


Скриншоты
ext_adm1.gif

ext_adm2.gif

ext_adm3.gif

ext_adm4.gif

ext_adm5.gif



Тема на моей тестовой площадке тынц.

P.S. Если кому-то очень хочется потренироваться, но нет времени/возможности тестировать на своём хостинге, по запросу посредством ЛС могу предоставить тестик на халявном хостингере (тестовый админ + юзер).

P.P.S. На кривых и самопальных хостингах имеет право не завестись.

Скачать:
extender_v_0_3_2.zip
(60.11 КБ) Скачиваний: 145

...устаревшие версии:
extender_v_0_1_8.zip
(50.51 КБ) Скачиваний: 581
extender_v_0_1_4.zip
(44.49 КБ) Скачиваний: 262
extender_v_0_1_2.zip
(42.74 КБ) Скачиваний: 246
Аватара пользователя
c61
Разработчик модов
 
Сообщения: 249
Зарегистрирован: 24.08.13 16:46

Сообщение c61 » 04.09.15 21:00

Обновил до версии 1.0.7. Очень интересно, как себя ведёт CodeMirror на разных браузерах и разрешениях. CodeMirror может глючить... и глючит. Нормально работает только в Опере 12. Просьба посмотреть на других браузерах... В FF ерунда, в Хроме более-менее. Хотфикс: в скрипте CodeMirror заменить везде lineWrapping на true, иначе совсем работать не будет.

Добавлено спустя 15 часов 42 минуты 5 секунд:
В 0.1.7 имеются непобедимые глюки с CodeMirror, скоро будет 0.1.8...

Добавлено спустя 7 часов 8 минут 40 секунд:
Выпустил 0.1.8. CodeMirror только в полноэкранном режиме.
Аватара пользователя
c61
Разработчик модов
 
Сообщения: 249
Зарегистрирован: 24.08.13 16:46

Сообщение c61 » 26.01.16 14:22

Выпустил 0.2.0:
  • устранены конфликты с обновленным twig в phpBB 3.1.6, приводящие к конфликтам с расширением "Быстрый ответ" и др., использующими AJAX;
  • добавлены проверки разрешения записи в необходимые для работы расширения каталоги.

Добавлено спустя 20 часов 43 минуты 15 секунд:
Выпустил версию 0.3.0:
  • реализована совместимость расширения с PHP 7 и phpBB 3.2;
  • добавлены проверки разрешения записи в необходимые для работы расширения файлы.
Аватара пользователя
c61
Разработчик модов
 
Сообщения: 249
Зарегистрирован: 24.08.13 16:46

Сообщение c61 » 01.02.16 16:30

Выпустил 0.3.1:
  • найдена и устранена ещё одна ошибка, приводящая к конфликту с расширением "Быстрый ответ" и др., использующими AJAX;
  • победив лень, внял замечаниям Sumanai и сделал-таки нормальную миграцию ))

Добавлено спустя 2 часа 55 минут 43 секунды:
Вдогонку выпустил версию 0.3.2:
  • по просьбам трудящихся: корректная миграция с любой версии, в предыдущей была только с 0.3.0
Аватара пользователя
c61
Разработчик модов
 
Сообщения: 249
Зарегистрирован: 24.08.13 16:46

Сообщение Shredder » 02.03.16 14:13

А нет ли конструктора самих событий? 8-) И возможно ли вообще его сделать? :lol:
Выполняю работы по phpBB3 на заказ. Пишите в личку или на почту.
Бесплатная поддержка - только в темах.
Shredder
Администратор
 
Сообщения: 841
Зарегистрирован: 22.08.13 09:05

Сообщение angst66 » 03.03.16 10:24

Попробовал на локалке, работает. Вопрос. Коды можно бесконечно добавлять в админке, и писать между ними пояснения?
Загрузил на реальный хостинг, вывалилось
Код: Выделить всё
[phpBB Debug] PHP Warning: in file [ROOT]/ext/c61/extender/event/listener.php on line 984: file_exists(): open_basedir restriction in effect. File(/tmp/Extender_ace51c9f_Template.lock) is not within the allowed path(s): (/var/www/angst66/data:.)
Страница настроек не открывается, ошибка в типе содержания.
angst66
 
Сообщения: 13
Зарегистрирован: 28.02.16 18:19

Сообщение Xisp » 03.03.16 20:44

Shredder писал(а):И возможно ли вообще его сделать? :lol:


Почему бы и нет. Тыкаешь в место в файле, он ищет в нём переменные и предлагает добавить их в событие (имя настраивается) при помощи галочек (мышью).
angst66 писал(а):open_basedir restriction in effect.

Мне за вас погуглить?
Xisp
 
Сообщения: 196
Зарегистрирован: 05.04.14 18:00

Сообщение Shredder » 16.04.16 13:58

angst66 писал(а):open_basedir restriction in effect.

Простым способом это не решается :evil: Мне помогло так (может зависеть от конфигурации конкретного сервера):
Найти в файлах extender/event/listener.php и extender/acp/extender_module.php все вхождения sys_get_temp_dir() и заменить их на ini_get('upload_tmp_dir')
Выполняю работы по phpBB3 на заказ. Пишите в личку или на почту.
Бесплатная поддержка - только в темах.
Shredder
Администратор
 
Сообщения: 841
Зарегистрирован: 22.08.13 09:05


Вернуться в Скрипты и расширения для phpBB 3.1.x

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1

cron