На форуме ip board 3.4.6 не так как нужно работает
bbcode hide - хук [WR] Скрытый текст v1.2.0.Вывод тега обычный:
[hide='20']скрытый текст для пользователей, которые написали больше 20 сообщений[/hide]
Вот должно работать по идеи, но если в кавычках - то работает не с кол-вом сообщений, а с именем пользователя.
Вывод
ошибку: "ВЫ должны быть пользователем с именем 20".
Если кавычки убрать -
работает как нужно.То есть проблема:
1) или в кавычках, которые подставляются сами. (по кавычкам разбор далее)
2) или в самом хуке, который не переводит числа в кавычках с переменной "имя_пользователя" в переменную "кол-во сообщений".
может в коде где ошибка? или что удалить для того, чтобы
осталось только hide для незарегистрированных и по кол-ву сообщений? (вырезаем привязку к ИД пользователя, к имени, к репутации - переменные $cond, если не ошибаюсь).
Разбор по кавычкам:На одном хука (hide) для ip board 3.4.* подставляются кавычки в тег.
За создание самого тега отвечает файл
class_private_data. Методом несколько дневных тестов определился с основными составляющими документа и кто за что отвечает.
Вот код
всего дока:[spoiler="код всего"]
<?php
class classPrivateData
{
protected $registry;
protected $DB;
protected $settings;
protected $lang;
protected $member;
protected $cache;
protected $css_added = false;
public $priv_data = array();
protected $tag = 'hide';
protected $post = array();
protected $cond_types = array('reg' => 'reg',
'g' => 'group',
'p' => 'posts',
'rep' => 'rep',
'r' => 'rep',
);
/**
* Constructor
*
* @return void
*/
public function __construct( ipsRegistry $registry )
{
#Seup shortcuts
$this->registry = $registry;
$this->DB = $this->registry->DB();
$this->settings =& $this->registry->fetchSettings();
$this->request =& $this->registry->fetchRequest();
$this->lang = $this->registry->getClass('class_localization');
$this->member = $this->registry->member();
$this->memberData =& $this->registry->member()->fetchMemberData();
$this->cache = $this->registry->cache();
$this->caches =& $this->registry->cache()->fetchCaches();
#Get language
$this->registry->getClass('class_localization')->loadLanguageFile( array( 'public_private_data' ), 'core' );
}
/**
* Проверить условия и добавить скрытый текст в тело сообщения
*
* @param array $row - как минимум, необходимы: 'post', 'private_data', 'author_id'.
* @return string
*/
public function showPrivateData($row)
{
$passed = 0;
$hash = $this->_thrawData($row['private_data']);
if(!is_array($hash) or !count($hash))
{
return $row['post'];
}
$this->post = $row;
//-----------------------------------------
// Последовательно обрабатываем каждый "скрытый текст"
//-----------------------------------------
foreach($hash as $id => $data)
{
//-----------------------------------------
// Опции, умолчания, параметры
//-----------------------------------------
$data['options'] = $data['options'] ? str_split($data['options']) : array();
#Свернут или развернут текст по умолчанию?
$data['state'] = in_array('c', $data['options']) ? 'collapsed' : $this->settings['priv_default_state'];
//-----------------------------------------
// Базовые условия
//-----------------------------------------
#Супермодераторы видят весь скрытый текст
if( $this->memberData['g_is_supmod'] )
{
$passed = 1;
}
#Смотрим свой собственный скрытый текст
elseif( $this->memberData['member_id'] and ( $this->memberData['member_id'] == $row['author_id']) )
{
$passed = 1;
}
//-----------------------------------------
// Указанные пользователем условия
//-----------------------------------------
else
{
$ret_conds = array();
#Получаем блоки условий
$blocks = explode(',', $data['conditions']);
#Последовательно проверяем все блоки
foreach($blocks as $b_id => $block)
{
$block_passed = 1;
foreach(explode('+', $block) as $cond)
{
$ret = $this->pdCheckCondition($cond);
if( ! $ret['passed'] )
{
$block_passed = 0;
}
$ret_conds[$b_id][] = $ret;
}
#Блок условий успешно выполнен
if( $block_passed )
{
$passed = 1;
}
}
//(print_r($ret_conds));
}
//-----------------------------------------
// Результат: УСПЕХ
//-----------------------------------------
if( $passed )
{
$row['post'] = str_replace('<!--pd_'.$id.'-->', $this->registry->getClass('output')->getTemplate('private_data')->private_data_opened( $data ), $row['post']);
if( ! $this->css_added )
{
ipsRegistry::getClass('output')->addToDocumentHead( 'importcss', ipsRegistry::$settings['public_dir'] . 'style_css/' . ipsRegistry::getClass('output')->skin['_csscacheid'] . '/private_data.css' );
$this->css_added = true;
}
}
//-----------------------------------------
// Результат: ПРОВАЛ
//-----------------------------------------
else
{
switch($this->settings['priv_hide_mode'])
{
case 1:
$replacer = '';
break;
case 2:
default:
$replacer = $this->registry->getClass('output')->getTemplate('private_data')->private_data_closed( $data, $ret_conds );
break;
}
$row['post'] = str_replace('<!--pd_'.$id.'-->', $replacer, $row['post']);
}
}
return $row['post'];
}
/**
* Проверить, имеем ли мы доступ к аттачу
*
* @param array $row - необходим 'attach_pd_conditions'
* @return integer
*/
public function checkAttachment($row)
{
#Аттач не был скрыт - сразу выходим
if( ! $row['attach_pd_conditions'] )
{
return true;
}
$this->post = $row;
$passed = 0;
//-----------------------------------------
// Базовые условия
//-----------------------------------------
#Супермодераторы видят весь скрытый текст
if( $this->memberData['g_is_supmod'] )
{
$passed = 1;
}
#Смотрим свой собственный скрытый текст
elseif( $this->memberData['member_id'] and ($this->memberData['member_id'] == $row['attach_member_id']) )
{
$passed = 1;
}
//-----------------------------------------
// Указанные пользователем условия
//-----------------------------------------
else
{
#Получаем блоки условий
$blocks = explode(',', $row['attach_pd_conditions']);
#Последовательно проверяем все блоки
foreach($blocks as $b_id => $block)
{
$block_passed = 1;
foreach(explode('+', $block) as $cond)
{
$ret = $this->pdCheckCondition($cond);
if( ! $ret['passed'] )
{
$block_passed = 0;
}
}
#Блок условий успешно выполнен
if( $block_passed )
{
$passed = 1;
}
}
}
return $passed;
}
/**
* Проверить выполнение одного условия
*
* @param string $cond - строковое представление условия
* @return array
*/
public function pdCheckCondition( $cond )
{
$type = null;
$passed = 0;
$reason = $this->lang->words['priv_incorrect_condition'];
$error = 0;
$cond = trim($cond);
//-----------------------------------------
// Определяем тип условия
//-----------------------------------------
if($cond)
{
#ID пользователя
if(is_numeric($cond))
{
$type = 'member_id';
}
#Конструкция
elseif( preg_match("#^([a-z]+)(?:=(.*))?#is", $cond, $match) and array_key_exists($match[1], $this->cond_types) )
{
$type = $this->cond_types[$match[1]];
$cond = $match[2];
}
#Имя пользователя
else
{
$type = 'member_name';
}
}
//-----------------------------------------
// Проверяем условие
//-----------------------------------------
$func = array($this, 'check_' . $type);
if(is_callable($func))
{
list($passed, $reason) = call_user_func($func, $cond);
}
else
{
$error = 1;
}
return array( 'type' => $type, 'passed' => $passed, 'reason' => $reason, 'error' => $error );
}
/**
* Готовит пост со скрытым текстом для редактирования
* There is no way we can use just $post['post'] cause of new tricky code from classPost.php
*
* Функция сильно полегчала со времен 2.х
* В общем случае, если мы можем редактировать пост, значит и скрытый текст в нем видим
*
* Также предполагается уже инициализированный класс бб-кодов
*
* @param string $text - текст поста с указателями
* @param array $post - массив с постом. Реально нужен только 'private_data'
* @return string
*/
public function preEditPrivateData( $text, $post )
{
if($post['private_data'])
{
foreach($this->_thrawData($post['private_data']) as $pd_id => $pd_data)
{
//$pd_data['conditions'] = ($pd_data['conditions'] != $this->settings['priv_default']) ? $pd_data['conditions'] : '';
$str = implode('|', array($pd_data['conditions'], $pd_data['title'], $pd_data['options']));
#Лень городить код -> убираем пустые секции
$str = preg_replace('#\|+$#', '', $str);
$text = str_replace('<!--pd_'.$pd_id.'-->', '[' . $this->tag . ($str ? ('=\'' . $str . '\'') : '') . ']' . IPSText::getTextClass('bbcode')->preEditParse($pd_data['text_bbcode']) . '[/'. $this->tag . ']', $text);
}
}
return $text;
}
/**
* Выцепить скрытый текст из поста и упаковать в 'private_data'
* Должно вызываться из classPost->compilePostData,
* т.к. требуется класс бб-кодов с текущими настройками
*
* @param $post
* @return array
*/
public function createPrivateData($post)
{
//------------------
// Пресечь попытки хитрых эксплойтов с аттачами
//------------------
if(strpos($post['post'], '[attachment=') !== false)
{
if(preg_match_all('#\[attachment=(\d+?):#', $post['post'], $out, PREG_PATTERN_ORDER))
{
$this->DB->build(array( 'select' => 'attach_id',
'from' => 'attachments',
'where' => "attach_id IN('". implode("','", $out[1]) ."') AND attach_pd_conditions!='' AND attach_member_id!='{$this->memberData['member_id']}'",
) );
$q = $this->DB->execute();
while( $attach = $this->DB->fetch($q) )
{
$post['post'] = str_replace("[attachment={$attach['attach_id']}:", "", $text);
}
}
}
//------------------
// Экономим ресурсы
// Не крутим регулярку на обычных постах, коих большинство
//------------------
if( false === strpos($post['post'], '['.$this->tag) )
{
return $post;
}
//------------------
// Создаем скрытый текст
//------------------
$post['post'] = preg_replace_callback( $q = "#(?:\[{$this->tag})=?(?:&quot;|&\#39;)?(.*?)(?:\|(.*?))?(?:\|(.*?))?(?:&quot;|&\#39;)?\](.*?)(?:\[\/{$this->tag}\])#siu", array( &$this, 'process'), $post['post'] );
if( count($this->priv_data) )
{
$post['private_data'] = $this->_freezeData($this->priv_data);
}
return $post;
}
/**
* Подготовить превью поста
* В этой функции хайд всегда будет виден из-за первых двух проверок в showPrivateData
*/
public function getPostPreview($post)
{
return $this->showPrivateData($this->createPrivateData($post));
}
/**
* Callback
*
* @param array $match
* @return string - текстовый указатель
*/
protected function process($match)
{
list($_full, $_cond, $_title, $_opts, $_text) = $match;
//var_dump($match);
//die();
//-----------------------------------------
// Проверочки
//-----------------------------------------
#Вложенный хайд невозможен!
$needle = '[' . $this->tag;
if( stristr($_cond, $needle) !== false or stristr($_text, $needle) !== false )
{
$this->registry->getClass('output')->showError( 'priv_error_tag_inside' );
}
#Группы
if($this->settings['priv_restrict_groups'] and !in_array($this->memberData['member_group'], explode(',', $this->settings['priv_groups'])))
{
$this->registry->getClass('output')->showError( 'priv_error_group' );
}
#Форумы
if($this->settings['priv_restrict_forums'] and !in_array($this->forum['id'], explode(',', $this->settings['priv_forums'])))
{
$this->registry->getClass('output')->showError( 'priv_error_forum' );
}
#Посты
if($this->settings['priv_posts'] and $this->memberData['posts'] < $this->settings['priv_posts'])
{
$this->registry->getClass('output')->showError( 'priv_error_posts' );
}
//-----------------------------------------
// Замена простых числовых условий
//-----------------------------------------
if($this->settings['priv_replace_numeric'] == 2)
{
$_cond = preg_replace('#(^|,)\s*(\d+)\s*(,|$)#is','$1p=$2$3', $_cond);
}
elseif($this->settings['priv_replace_numeric'] == 3) //reputation added :)
{
$_cond = preg_replace('#(^|,)\s*(\d+)\s*(,|$)#is','$1r=$2$3', $_cond);
}
//-----------------------------------------
// Условия по умолчанию
//-----------------------------------------
$_cond = $_cond ? $_cond : $this->settings['priv_default'];
//-----------------------------------------
// Title
//-----------------------------------------
if($_title)
{
$_title = IPSText::truncate($_title, 100);
}
//-----------------------------------------
// IMPORTANT: Проверка корректности условий
//-----------------------------------------
if($this->settings['priv_validate_options'] and ! $this->validateCond($_cond))
{
$this->registry->getClass('output')->showError( 'priv_err_condition_1' );
}
//-----------------------------------------
// Записать в массив
//-----------------------------------------
#Чтобы не вызывать парсер лишний раз, мы храним сразу две версии скрытого текста
#С бб-кодами - для редактирования
#С html-кодом, для отображения
#В версии 1.2 добавлены опции и title
$this->priv_data[] = array( 'conditions' => $_cond,
'options' => $_opts,
'title' => $_title,
'text_bbcode' => $_text,
'text_parsed' => IPSText::getTextClass( 'bbcode' )->preDisplayParse( $_text ),
);
//-----------------------------------------
// Обработать аттачи внутри скрытого текста
//-----------------------------------------
if(strpos($_text, '[attachment=') !== false)
{
if(preg_match_all('#\[attachment=(\d+?):#', $_text, $out, PREG_PATTERN_ORDER))
{
$this->DB->update('attachments', array('attach_pd_conditions' => $_cond), "attach_id IN ('". implode("','", $out[1]) ."') AND attach_member_id='{$this->memberData['member_id']}'");
}
}
#Не превысили ли ограничение по кол-ву?
if($this->settings['priv_max_number'] and count($this->priv_data) > $this->settings['priv_max_number'])
{
$this->registry->getClass('output')->showError( array( 'priv_error_max_number', intval($this->settings['priv_max_number']) ) );
}
#Возвращаем указатель
return '<!--pd_'. (count($this->priv_data) - 1) .'-->';
}
/**
* Проверить валидность условий
*/
protected function validateCond($str)
{
#Получаем блоки условий
$blocks = explode(',', $str);
#Последовательно проверяем все блоки
foreach($blocks as $b_id => $block)
{
foreach(explode('+', $block) as $cond)
{
$ret = $this->pdCheckCondition($cond);
if( $ret['error'] )
{
return false;
}
}
}
return true;
}
/**
* ID пользователя
*/
protected function check_member_id($val)
{
if( intval($val) == $this->memberData['member_id'] )
{
$passed = 1;
}
$reason = sprintf($this->lang->words['priv_reason_member_id'], $val);
return array($passed, $reason);
}
/**
* Имя пользователя
*/
protected function check_member_name($val)
{
if($this->memberData['member_id'] and !is_numeric($val) and $val == $this->memberData['members_display_name'])
{
$passed = 1;
}
$reason = sprintf($this->lang->words['priv_reason_member_name'], $val);
return array($passed, $reason);
}
/**
* Регистрация
*/
protected function check_reg($val)
{
if($this->memberData['member_id'] and $this->memberData['member_group'] != $this->settings['auth_group'])
{
$passed = 1;
}
$reason = sprintf($this->lang->words['priv_reason_reg'], $this->base_url);
return array($passed, $reason);
}
/**
* Группа
*/
protected function check_group($val)
{
//var_dump($val, $this->memberData);
#By id?
if(is_numeric($val) and $this->memberData['member_group_id'] == $val)
{
$passed = 1;
}
#By title?
elseif(!is_numeric($val) and $this->memberData['g_title'] == $val)
{
$passed = 1;
}
//var_dump($this->caches['group_cache']);
$g_title = is_numeric($val) ? $this->caches['group_cache'][$val]['g_title'] : $val;
$reason = sprintf($this->lang->words['priv_reason_group'], $g_title);
return array($passed, $reason);
}
/**
* Посты
*/
protected function check_posts($val)
{
if($this->memberData['posts'] >= $val)
{
$passed = 1;
}
else
{
$remains = sprintf($this->lang->words['priv_remains'], $val - $this->memberData['posts']);
}
$reason = sprintf($this->lang->words['priv_reason_posts'], $val) . $remains;
return array($passed, $reason);
}
/**
* Репутация
*/
protected function check_rep($val)
{
if($val and $this->memberData['pp_reputation_points'] >= $val) //points
{
$passed = 1;
}
elseif(!$val and isset($this->post['has_given_rep']) and $this->post['has_given_rep'] == '1') //like old 'thanks' :)
{
$passed = 1;
}
if($val)
{
$remains = sprintf($this->lang->words['priv_remains'], $val - $this->memberData['pp_reputation_points']) . $remains;
$reason = sprintf($this->lang->words['priv_reason_rep_points'], $val) . $remains;
}
else
{
$reason = $this->lang->words['priv_reason_rep'];
}
return array($passed, $reason);
}
protected function _freezeData($data)
{
return is_array($data) ? serialize($data) : null;
}
protected function _thrawData($data)
{
$data = unserialize($data);
return is_array($data) ? $data : array();
}
}
?>
[/spoiler]
Вот код, который как раз отвечает за
создание тега и его работу:[spoiler="код создание тега"]
$post['post'] = preg_replace_callback( $q = "#(?:\[{$this->tag})=?(?:&quot;|&\#39;)?(.*?)(?:\|(.*?))?(?:\|(.*?))?(?:&quot;|&\#39;)?\](.*?)(?:\[\/{$this->tag}\])#siu", array( &$this, 'process'), $post['post'] );
[/spoiler]
Вот код, который
отвечает за редактирование:[spoiler="редактирование"]
$text = str_replace('<!--pd_'.$pd_id.'-->', '[' . $this->tag . ($str ? ('=\'' . $str . '\'') : '') . ']' . IPSText::getTextClass('bbcode')->preEditParse($pd_data['text_bbcode']) . '[/'. $this->tag . ']', $text);
[/spoiler]
Если убрать кавычки, то они не будут подставляться при
редактировании:
[spoiler="редактирование2"]
$text = str_replace('<!--pd_'.$pd_id.'-->', '[' . $this->tag . ($str ? ('=' . $str . '') : '') . ']' . IPSText::getTextClass('bbcode')->preEditParse($pd_data['text_bbcode']) . '[/'. $this->tag . ']', $text);
[/spoiler]
Но изначально при создании кавычки все равно добавляются, где они могут быть здесь:
[spoiler="дважды quot и 39"]
$post['post'] = preg_replace_callback( $q = "#(?:\[{$this->tag})=?(?:&quot;|&\#39;)?(.*?)(?:\|(.*?))?(?:\|(.*?))?(?:&quot;|&\#39;)?\](.*?)(?:\[\/{$this->tag}\])#siu", array( &$this, 'process'), $post['post'] );
[/spoiler]
Регулярку на 100% не расшифровал, вижу ASCII = $quot; &#39; если их удалю, то сам тег создается, но кавычки все равно остаются.
Пробовал перед этой регуляркой написать свою, чтобы кавычки удаляло, но код перестает работать (скорее мой preg-replace не правильный, но если что его потом обсудим).
Сейчас проблема:
убрать кавычки при создании hide.
з.ы. в хуке еще есть файлы .xml, по моему мнению там ничего из проблемы нет, но если - могу выложить все файлы, или хук архивом.
Тому кто поможет - денежное вознаграждение :)