Перенаправление внешних ссылок на промежуточную страницу
В комментариях к записи о HTML Purifier был задан вопрос о том, каким образом можно переделать внешние адреса на переход через промежуточную страницу на своём сайте. Сделаем функционал, обрабатывающий внешние ссылки в тексте и переадресующий их на страницу подтверждения перехода на другие сайты.
Для решения этой задачи напишем компонент, подставляющий произвольный префикс к адресам всех внешних ссылок в контенте сайта. Будем считать внешними все ссылки, начинающиеся с http://
, https://
и ftp://
. Остальные ссылки вроде mailto:
и прочих будем игнорировать. Для удобства сделаем настраиваемыми протоколы и префикс:
В простейшем случае обработку ссылок можно производить прямо при выводе текста в представлении:
<?php echo DOuterLinker::load()->process($post->text);
Для настройки компонента можно использовать методы addProtocols()
, setProtocols()
и setPrefix()
в любом сочетании:
echo DOuterLinker::load()->setPrefix('/link?a=')->process($html); echo DOuterLinker::load()->addProtocols(array('dc'))->setPrefix('/link?a=')->process($html); echo DOuterLinker::load()->setProtocols(array('http', 'https'))->process($html);
Также можно работать классически
$linker = new DOuterLinker(); $linker->setProtocols(array('http')); $linker->setPrefix('/link?a='); echo $linker->process($html);
Чтобы не записывать одни и те же настройки каждый раз можно переопределить их в своём классе OuterLinker
class OuterLinker extends DOuterLinker { protected $_protocols = array('http', 'https'); protected $_prefix = '/site/link?url='; }
и использовать его вместо оригинального:
<?php echo OuterLinker::load()->process($post->text);
Для уменьшения нагрузки лучше обрабатывать текст всего один раз перед сохранением записи в базу данных.
Рассмотрим организацию преобразования текста перед сохранением записи на примере модели фреймворка Yii.
Пример использования в модели Yii
Пусть в нашей модели есть поля text
для исходного HTML кода и purified_text
для обработанного. Добавим в модель методы beforeSave()
и afterFind()
, в которых будем производить замену ссылок:
class Post extends CActiveRecord { protected function beforeSave() { if (parent::beforeSave()) { $this->purified_text = DOuterLinker::load()->process($this->text); return true; } else return false; } protected function afterFind() { if (!$this->purified_text) $this->purified_text = DOuterLinker::load()->process($this->text); parent::afterFind(); } }
Теперь в представлении нужно вывести результат:
<?php echo $post->purified_text;
Если необходимо использовать данную функцию совместно с DPurifyTextBehavior, то методы нужно немного изменить. Обработка ссылок должна производиться после работы DPurifyTextBehavior. Чтобы не сохранять два раза результат в момент afterFind
отключим автосохранение у поведения 'updateOnAfterFind'=>false
и будем сохранять его вручную вызовом $this->updateModel()
(это вызов метода DPurifyTextBehavior::updateModel()
):
class Post extends CActiveRecord { public function behaviors() { return array( 'PurifyText'=>array( 'class'=>'DPurifyTextBehavior', 'sourceAttribute'=>'text', 'destinationAttribute'=>'purified_text', 'purifierOptions'=> array( 'Attr.AllowedRel'=>array('nofollow'), 'HTML.SafeObject'=>true, 'Output.FlashCompat'=>true, ), // отключим автосохранение результата // так как будем это делать вручную 'updateOnAfterFind'=>false, ), ); } protected function beforeSave() { // сначала отработают все поведения if (parent::beforeSave()) { // потом мы произведём замену ссылок if ($this->purified_text) $this->purified_text = DOuterLinker::load()->process($this->purified_text); return true; } else return false; } protected function afterFind() { // запомним, заполнено ли поле с результатом $isEmpty = $this->purified_text ? true : false; // запустим все поведения parent::afterFind(); // а потом если результат обновился if ($isEmpty && $this->purified_text) { // произведём замену ссылок $this->purified_text = DOuterLinker::load()->setPrefix('site/link?url=')->process($this->purified_text); // и вызовем метод DPurifyTextBehavior::updateModel() сохранения результата $this->updateModel(); } } }
Теперь в тексте все внешние ссылки
<a href="http://www.yandex.ru?q=query&lang=ru">Yandex</a>
преобразуются в
<a href="/site/link?url=http://www.yandex.ru%3Fq%3Dquery%26lang%3Dru">Yandex</a>
Теперь достаточно добавить перенаправляющий экшен
class SiteController extends Controller { public function actionLink($url) { // ... } }
в котором уже выводить свой текст и настоящую ссылку.
Кроме своей страницы все ссылки можно перенаправлять и на чужую:
echo DOuterLinker::load()->setPrefix('http://anonym.to/?')->process($html);
Это пример использования сервиса anonim.to, производящего перенаправление на переданный ему адрес. Сайты, на которые Вы ссылаетесь, не смогут понять с какого сайта к ним перешли, так как в поле HTTP_REFERER у них будет отображаться http://anonim.to вместо адреса вашего сайта.
Спасибо. Отличная статья
спасибо. интересные статьи!
Жаль, что я не знаком с ООП, программирую только структурно
Действительно жаль. Держите нас в курсе.
В конце DOuterLinker.php лучше сдеать так, чтобы закрывать только действительно внешние ссылки
Смысл в том чтобы добавить проверку на наличие в сылке своего домена.
Добрый день!
Проблема:
На странице есть ссылка, которая меняет статус какой-либо модели:
Содержимое страницы обновляется через Ajax.
В контроллере:
При этом, когда кликаешь по этой ссылке, адрес страницы соответственно меняется с ".../model/view?id=1" на ".../model/set-status?id=1&status=2"
Подскажите пожалуйста как сделать так чтобы страница обновилась через Ajax и не поменялась адресная строка? И если есть возможность то не использовать POST-запрос
Подскажите пожалуйста каким плагином реализована такая задержка при переходе по внешней ссылке с показом рекламы на этом сайте? https://xn-----7kckegeaw8apdfn0d9a0j.xn--p1ai/goto?link=
Обычный JavaScript: