Николай Ланец
25 янв. 2014 г., 22:57

modCaptcha

В свое время мы с Ваней ака vanchelo работали над парочкой проектов, и там надо было использовать каптчу. Гугловую рекаптчу не охото было использовать, так как лень было регистрировать сайт в гугле, и Ваня там запилил свою каптчу. Но она не была оформлена в установочный пакет. Сегодня я подправил пару моментов, добавил системные настройки, чанк для вывода картинки, сниппет и процессор для проверки кода и оформил это в пакет modCaptcha.
Все работает. Ване огромное спасибо за исходный код!
Вызов каптчи: просто чанк modcaptcha.
Проверка доступна двумя способами: 1. Процессором. 2. Сниппетом. Данный сниппет все равно в итоге вызывает тот же процессор, но разница в том, что в него не обязательно передавать значение полученного кода параметром. Если код не был передан явно, сниппет попытается получить его из массива $_REQUEST['php_captcha'].
При этом, если код верный, сниппет должен вернуть строку 'true'. Процессор же не должен вернуть $response->isError().
P.S. Проект на Github: github.com/Fi1osof/modCaptcha
UPD: Выпустил modCaptcha-1.0.1-beta, в которой фиксится бага с некорректным завершением сессий (как минимум на modxcloud.com). Об этом писал здесь: modxclub.ru/blog/dokumentatsiya-dlya-spetsialistov/36.html
Отличное дополнение! Его очень не хватало… Спасибо вам большое ребята!
Пожалуйста!
Обкатаете — дайте знать все ли работает как надо.
да я на тестовом сайте сразу же попробовал))). Все работает как часики. Проверил как различает строчные и заглавные буквы… Все работает, все ОК! СПС большое!
Ну и славненько :) Собственно, есть же и другие пакеты. Но мы сталкивались с проблемой, что на modxcloud из-за их особенностей с сессиями каптча не работала (потому что сессия не корректно сохранялась). Андчир вот тоже недавно выпустил свою каптчу, но у него там ремарка записана:
Attention! Current CAPTCHA need to the standard PHP sessions. «System Settings» -> «Sessions and Cookies» -> «session_handler_class» clean (make blank).
Вот мы попытались сделать так, чтобы этих проблем не было, и надеюсь, получилось.
Спасибо за хорошее дополнение! Еще бы здесь прямо демку сделали вообще класс было бы! )) Сразу просто увидеть демонстрацию было бы нагляднее)))
Будет со временем демка :)
Почему-то на картинке отрисовываются только линии, а текста нет. В чем может быть проблема?
Шрифты не отрисовываются. Но почему — не ясно. Они там прикладываются сразу в пакете. Может настройка количества символов не корректно прописалась? Пришли доступы в личку.
Отправил в личку. Капча показывается на страничке регистрации.
В логи MODX-а пишет:
[2014-03-12 09:55:25] (ERROR @ ...../core/components/modcaptcha/php-captcha.inc.php: 295) PHP warning: imagefttext() [function.imagefttext]: Could not find/open font
Сейчас на семинаре, и к сожалению не могу более детально изучить проблему, придется тебе самому еще покопать. Судя по всему какая-то ошибка в формировании путей к шрифтам.
Да, только проблема была в файле assets/components/modcaptcha/captcha.php, где шрифты объявляются, я добавил полный путь, и все заработало:
$fonts = array( dirname(__FILE__).'/fonts/VeraBd.ttf', dirname(__FILE__).'/fonts/VeraIt.ttf', dirname(__FILE__).'/fonts/Vera.ttf');
Странно. Я на быструю руку именно это и попробовал, но что-то не помогло, только пятерки появились. Ну заработало — славненько. В новой версии пропишем полные пути.
Добрый день, как совместить с FormIt?
Можете рассказать хотябы в кратце? Не могу найти информацию как его использовать.
Если каптча нужна для формы регистрации то делаем так: 1. Копируем сниппет «modcaptcha.check_captcha», назовем его «modcaptcha.preHook» и немного меняем ему код, эти строки:
if($response->isError()){ $result = $response->getMessage(); } else{ $result = 'true'; }
меняем на эти:
if($response->isError()){ $result = false; } else{ $result = true; }
Это нужно для Login'а ибо прехук должен возвращать булево значение.
2. В вызов сниппета Register добавляем 2 параметра:
&preHooks=`modcaptcha.preHook` &captcha_key=`mycaptcha`
первый это сниппет для прехука, второй ключ каптчи 3. В форму регистрации добавляем
<input type="text" name="mycaptcha"> [[!$modcaptcha? &captcha_key=`mycaptcha`]]
После этих манипуляций должно работать.
PS У меня версия modx 2.3.3 там modCaptcha не заработал. Пришлось подправить немного процессор «modcaptcha/web/check», строку
$code = $this->getProperty('code');
заменил на
$code = $this->modx->request->parameters[$this->getProperty('method')][$key];
Не понятно ошибка это была или раньше modProcessor подгружал в свойства переменные из глобальных массивов, но в версии 2.3.3 этого не происходит.
PS У меня версия modx 2.3.3 там modCaptcha не заработал. Пришлось подправить немного процессор «modcaptcha/web/check»,
Странно, у меня все работает, и не на одном сайте. Убедитесь что у вас последние версии пакетов modxSite и modCaptcha.
Не понятно ошибка это была или раньше modProcessor подгружал в свойства переменные из глобальных массивов, но в версии 2.3.3 этого не происходит.
Если вы процессор через коннектор вызываете, то там глобальные устанавливаются (на уровне modConnectorRequest или modConnectorResponse). Если вы просто вызываете процессор через $modx->runProcessor(), то тогда в процессоре глобальных не будет. Их там и не было никогда. Надо явно тогда в него в вызов передавать $_REQUEST|$_GET|$_POST.
modCaptcha 1.0.3-beta поставил вчера из репозитория modx, modxSite 1.3.1-beta
Да вызывал напрямую в смарти-шаблоне через {processor}. Да и сниппет вызывает точно так же.
В теме же и сказано вызывайте через процессор или сниппет =)
Я убрал вот этот код из сниппета:
$key = $scriptProperties['captcha_key']; if(empty($scriptProperties['code'])){ $param = "_".strtoupper($method); if(isset($$param)){ $p = $$param; $scriptProperties['code'] = (!empty($p[$key]) ? $p[$key] : ""); } }
И добавил вот такой в процессор.
public function process(){ $key = $this->getProperty('captcha_key'); $method = $this->getProperty('method'); $code = $this->getProperty('code'); if(empty($code)){ $code = $this->modx->request->parameters[$method][$key]; } $session_code = $_SESSION[$key];
В итоге параметр переданный из формы получается через modRequest, если 'code' не передан в процессор через настройки сниппета или процессора.
Кстати ещё один момент по поводу переменных переменных и суперглобальных массивов.
«Warning Please note that variable variables cannot be used with PHP's Superglobal arrays within functions or class methods. The variable $this is also a special variable that cannot be referenced dynamically. » php.net/manual/en/language.variables.variable.php
public function process(){         $key = $this->getProperty('captcha_key');         $method = $this->getProperty('method');         $code = $this->getProperty('code');         if(empty($code)){             $code = $this->modx->request->parameters[$method][$key];         }         $session_code = $_SESSION[$key];
Вот так делать и не надо. Не надо прописывать в процессоре работу с глобальными массивами запросов, а то как вы будете разграничивать обработку, когда у вас на странице больше одного вызова будет? Надо писать что-то типа {processor… params=$smarty.get}, то есть явно в вызов процессора передавать данные запроса.
Почему нет? параметр в $_REQUEST уникален, а если ключи разные то каждый вызов получит свой, мы ведь передаем в вызов «captcha_key» он и служит ключём в $_REQUEST, собственно как и «method» говорит в каком массиве искать.
Только что ради эксперимента создал два вызова процессора в шаблоне, создал 2 поля ввода и вывод 2-х картинок всё с разными «captcha_key». Каждый экземпляр отработал независимо от другого. =)
создал 2 поля ввода и вывод 2-х картинок всё с разными «captcha_key». Каждый экземпляр отработал независимо от другого. =)
Для этого параметр captcha_key и создавался. Но еще раз: прописывать внутри процессора работы с глобальными переменными запросов — не правильно. Но спорить с вами не буду. Вы можете делать так, как вам больше нравится.
А тут и не производиться работа с глобальными переменными, это уже сделал modRequest, мы лишь спрашиваем у него переменную по ключу и имени массива, и то только в случае если в параметрах процессора не был найден параметр «code». Ведь он уже инстанцирован и содержит все эти данные и без нас. Почему не использовать? Ответ «не правильно» без аргументации не ответ имхо.
Гуглите «почему глобальные переменные плохо».
стоп стоп. Не путайте меня =) я нигде и не использовал глобальные переменные о которых вы говорите =)
переменные $method и $key получаются из Properties процессора, их значения по умолчанию задаются в initialize(), и перезаписываются в случае если были переданы через $scriptProperties. А вот уже основываясь на этом я получаю значение $code в $modx->request который в свою очередь обрабатывает массивы $_REQUEST и т.д. И это не я придумал, а создатели modx =)
Где тут глобальные переменные которые «плохие»? ))
плохие глобальные переменные это
$a = 111; class A { public function myfunc() { global $a; echo $a; } }
У меня такое чувство что мы с вами говорим о разных вещах.
Ведь даже вот тут github.com/MODX-Club/modCaptcha/blob/master/core/components/modcaptcha/processors/modcaptcha/web/check.class.php строка 29, и это не мой код, а ваш =)
$session_code = (!empty($_SESSION[$key]) ? $_SESSION[$key] : '');
$_SESSION это суперглобальный массив =) Почему $_SESSION это не плохо, а $this->modx->request->parameters[$method][$key] плохо?
плохие глобальные переменные это $a = 111; class A {     public function myfunc() {         global $a;         echo $a;     } }
$_REQUEST: Замечание: Это 'суперглобальная' или автоматическая глобальная переменная. Это просто означает что она доступна во всех контекстах скрипта. Нет необходимости выполнять global $variable; для доступа к ней внутри метода или функции.
Где тут глобальные переменные которые «плохие»? ))
$this->modx->request->parameters[$method][$key];
Метод modRequest::getParameters() работает с глобальными переменными, а значит работая с $this->modx->request->parameters вы работаете с глобальными переменными. А надо использовать методы $this->getProperties()/$this->getProperty().
Почему $_SESSION это не плохо, а $this->modx->request->parameters[$method][$key] плохо?
Сессия одна на все области видимости скрипта. Ее нельзя ни с чем спутать. Работая с сессией вы априори знаете, что работаете только с ней. А вот обрабатывая данные запроса в процессоре, вы рискуете обработать данные не своего запроса. Сейчас, с каптчей, вы рассчитываете только на то, что у вас есть уникальный ключ параметра запроса и типа это вас перестраховывает. Да, это отчасти так, но это всего лишь меньший риск при использовании неправильного подхода. Он тем и не правильный, что при большей вероятности неуникального ключа, возрастает вероятность и логической ошибки. Если вы используете правильный подход, он всегда вам больше дает шанса на то, что и результат будет правильный.
Но вот вам еще один вариант: попробуйте свой скрипт вызвать не со страницы, а через Console. Будет ли у вас там нужный? $this->modx->request->parameters[$method][$key]; И что вы будете делать, чтобы у вас этот параметр был в пост-запросе? Как отладку будете выполнять?
Легко! Код в консоли:
<?php $_REQUEST['mykey'] = '123'; $method = 'REQUEST'; $key = 'mykey'; echo $modx->request->parameters[$method][$key];
вывод:
123
по умолчанию $method задан в вашем же процессоре и его значение 'REQUEST', но в случае если процессор был запущен вот так
$scriptProperties['method'] = 'GET'; $modx->runProcessor('modcaptcha/web/check', $scriptProperties, array( 'processors_path' => $path.'processors/', ))
То параметр будет перезаписан в параметрах modProcessor согласно вот этому куску кода: modprocessor.class.php
function __construct(modX & $modx,array $properties = array()) { $this->modx =& $modx; $this->setProperties($properties); }
А вот так туда попадуют параметры по умолчанию, и если заметить они не перезаписывают те что передаются при инициализации через $scriptProperties
public function setDefaultProperties(array $properties = array()) { $this->properties = array_merge($properties,$this->properties); return $this->properties; }
Точно так же в параметры процессора попадают все остальные параметры.
Единственное что бы я поправил, и у себя поправлю это все таки сделаю вот так:
$code = $modx->request->getParameters($key, strtoupper($method));
в методе getParameters клсса modRequest нет привидения к верхнему регистру как ни странно.
Легко! Код в консоли: $_REQUEST['mykey'] = '123'; $method = 'REQUEST'; $key = 'mykey'; echo $modx->request->parameters[$method][$key];
И опять работа с глобальной переменной… Кто бы сомневался.
по умолчанию $method задан в вашем же процессоре и его значение 'REQUEST'
Очевидно, что это рудимент, который, судя по истории процессора, даже никогда не использовался. Надо просто удалить будет.
Еще раз: делайте как вам больше нравится. Это из серии совета. Все на ваше усмотрение.
кстати совсем забыл по поводу вот этого:
Сессия одна на все области видимости скрипта.
Все суперглобальные массивы имеют один экземпляр на все области видимости =), а $_REQUEST, $_GET, $_POST, $_COOKIE и т.д. это все спуреглобальные переменные на ряду с $_SESSION
Единственное это то что данные в $_REQUEST, $_GET, $_POST, $_COOKIE нужно фильтровать ибо они не безопасны. Но так как в нашем случае идет обычное сравнение то фильтрация это излишний функционал.
И опять работа с глобальной переменной… Кто бы сомневался.
Ну если вы так желаете, то: код в консоли:
<?php $method = 'REQUEST'; $key = 'action'; echo $modx->request->getParameters($key, strtoupper($method));
вывод:
exec
'action' это POST переменная передаваемая коннектору консоли, а 'exec' её значение )
Очевидно, что это рудимент, который, судя по истории процессора, даже никогда не использовался. Надо просто удалить будет.
Ну учитывая то что есть REQUEST это понятно. Не понятно то, что вы вместо того чтобы развивать компонент доказываете мне то чего нет. =) Я лично вам не нравлюсь, или ваша идеология программирования не совпадает с моей, я не знаю. Но факт в том что ничего «не правильного» и «плохого» в использовании modRequest внутри процессора нет, тем более что он уже там есть в объекте $this->modx->request.
Не понятно то, что вы вместо того чтобы развивать компонент доказываете мне то чего нет. =)
Куда развивать? Вы будете учить как и что развивать? Вам не кажется это наглостью? Вам который раз говорится, что так не правильно, а вы гнете свое. Вы имеете право не использовать то, что мы предоставляем. Но учить как «правильно» в своем убеждающем формате — это уж извольте. Диалог закончен.
«это не правильно потому-что я так сказал» — вот ваш ответ между строк.
Как бы я не просил вас аргументировать ваш ответ, вы кроме придирок ничего так и не ответили. А сейчас просто сливаете разговор чтобы не оказаться не правым.
А по-поводу "… развивать ваш проект.." я написал специально чтобы посмотреть действительно ли задето ваше самолюбие, и вы это подтвердили. Вы уж извините меня за это. Я лишь хотел понять причину почему $_SESSION можно, а $_REQUEST нельзя.
Чтож пусть будет так. А пользоваться вашими компонентами я буду и впредь ибо они мне нравятся намного больше чем аналоги, хоть иногда и случаются вот такие казусы =)
Я лишь хотел понять причину почему $_SESSION можно, а $_REQUEST нельзя.
Я вам ответил почему. Вы это не приняли как причину. Ваше право. Я не буду усиленно объяснять вам свою точку зрения. Но ваши пожелания не буду внедрять, потому как они мне будут мешать (это по опыту работы). Вполне вероятно и вы к этому когда-нибудь придете. А может и не придете. Сейчас в компоненте ошибки нет. В той же сборке ShopModxBox проверка каптчи вызывается внутри процессора формы, в который передаются данные запроса явно. Единственное что в том процессоре следует доработать, так это передачу еще и параметра «captcha_key» (просто этот параметр появился позже, чем был написан тот процессор).
А где инструкция как её пользовать?
В статье, вроде, информации достаточно в статье. Просто устанавливается компонент и чанк вставляется. Но все было давно, сейчас не знаю на сколько это все работоспособное.
Ну вот всё поставил как в статье. Картbнка каптчи да появляется но форма отправляется не зависимо вписываю я код с картинки или нет хотя &preHooks=`modcaptcha.preHook`
&captcha_key=`mycaptcha` стоят на вызове сниппета... Сниппет AjaxForm вызываю с Formit.

Я уже сейчас так наверняка не скажу, но скорее всего это реализация для использования в процессорах. Во всяком случае в нашем form-процессоре использовалась точно: https://github.com/MODX-Club/modxSite/blob/master/core/components/modxsite/processors/site/web/form.class.php

Больше ничего не подскажу, давно уже с MODX не работаю.

Добавить комментарий