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 https://wiki.phpbb.com/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 https://wiki.phpbb.com/Event_List#Template_Events и phpBB Wiki: Event List: ACP Template Events https://wiki.phpbb.com/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 -->
Установка
Копировать содержимое каталога 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 -->
Объявление для гостей (боты и зарегистрированные пользователя игнорируются)
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=" × " 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});" /> <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 -->
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&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 -->
Код: Выделить всё
// Проверка установки языковых переменных
<!-- EVENT overall_footer_copyright_append -->
{L_MYLANGVAR1} {L_MYLANGVAR2}
<!-- ENDEVENT -->
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"> </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 -->
Код: Выделить всё
<!-- 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 -->
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 . '&t=' . $topic_id . '&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 . '&t=' . $topic_id . '&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 . '&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&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 -->
Минипрофили слева в 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 -->
Код: Выделить всё
<!-- 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 -->
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 -->
Код: Выделить всё
<!-- 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 -->
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 -->
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 -->
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 -->
Код: Выделить всё
<!-- EVENT PRE overall_footer_body_after -->
<!-- PHP -->
echo '$myvar = '.$myvar;
<!-- ENDPHP -->
<!-- ENDEVENT -->
Для тестрования снимите комментарий с ошибочного оператора.
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 -->
История версий
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...
При переходе на новую версию нет необходимости удалять настройки расширения, достаточно его отключить, удалить на сервере файлы расширения, записать файлы новой версии, затем включить расширение и из настройки расширения нажать "Сохранить изменения".
Скриншоты
Тема на моей тестовой площадке тынц.
P.S. Если кому-то очень хочется потренироваться, но нет времени/возможности тестировать на своём хостинге, по запросу посредством ЛС могу предоставить тестик на халявном хостингере (тестовый админ + юзер).
P.P.S. На кривых и самопальных хостингах имеет право не завестись.
Скачать: ...устаревшие версии: