Загрузка...

Капча в пользовательских формах

Не так давно мне потребовалось установить капчу на сайте клиента, для этого я конечно же пошел в «Плагины» и стал искать там подходящие варианты. Вариантов оказалось множество, но все они подходили только для того, чтобы отметить галочку и подключить готовую капчу в систему публикации комментариев, форму входа в админ панель и другое, никак при этом не предоставляя API или хоть какие бы то ни было функции для обработки данных, или же о них знали только сами авторы! То есть никакой вразумительной документации или описания их творении.

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

Вот для этих целей, пришлось написать собственный плагин, предназначение которого только пользовательские формы, так как готовых встраиваемых капч полно, а таких оказывается маловато. Если не прав — можете осудить меня в комментариях и прислать пример таких капч, буду рад если, таковые все же есть.

Документация

Плагин называется «JBlog Captcha», просто скачайте, активируйте и перейдите сразу в Настройки — JBlog Captcha, в админке WordPress. На странице настроек можно задать цвет шрифта и выбрать фон для капчи, там же представлен режим предпросмотра, как ваша капча будет выглядеть в публичной части сайта. Самое интересное, это то что вы сами регулируете сложность и вид капчи, комбинируя сочетания различных фоновых рисунков и цвет символов.

Данный плагин больше подойдет для разработчиков, конструирующих свои дефолтные формы на сайте, предназначенные для передачи данных и обработки их на сервере. Пример создания ajax формы обратной связи с данной капчей можно посмотреть в следующей заметке. Знание основ программирования на PHP и JS будет являться дополнительным преимуществом при использовании плагина. Если же вам нужна капча, которая подключается к комментариям и другим стандартным формам, которые генерирует WordPress, то пожалуйста, воспользуйтесь поиском таких плагинов по базе репозитория, уверяю, их там предостаточно.

Оф. ссылка: ru.wordpress.org/plugins/jblog-captcha

Пример. Простое использование:

if(class_exists('JBlogCaptcha')){
   echo do_shortcode("[jbcptch]");
}

Что покажет шорткод

Выводит изображение и поле для ввода:

Капча

Делаем форму с капчей

Но смысла в выводе формы, без обработки мало. Добавим кнопку «Отправить», дадим ей id #sendButton.

<div class="inner-form-captcha">
   <?php
      if(class_exists('JBlogCaptcha')){
         echo do_shortcode("[jbcptch]");
      }
   ?>
   <button id="sendButton">Проверить</button>
</div>

Вешаем обработку отправки

Если используем AJAX, что предпочтительно, то при наличии jQuery можно написать следующее:

$('#sendButton').on('click', function () {

    /*
     * Берем значение, которое ввел пользователь в поле ввода:
     */
    var string = $('#jbcptcha_input').val(),
        pathToHandlers = '/wp-content/themes/twentyseventeen/handlers/'; // этот путь у вас будет свой!

    /*
     * Посылаем POST для проверки на сервер
     * pathToHandlers — путь до файла обработчика
     * Принимаем результат (true/false) и делаем какие либо действия:
     */
    $.post(pathToHandlers + "check-captcha.php", {str: string}, function (data) {
        if (data.res) {
            /*
             *   капча введена верно
             *   делаем действия на клиенте, js
             */
            alert('капча введена верно');
        } else {
            /*
             *   капча введена НЕ верно
             */
            alert('капча введена неверно!');
        }
        /*
         * Перезагружаем картинку в браузере
         * */
        var img = $('#generate-jbcptcha img'),
            src = img.attr('src').split('?');
        img.attr('src', src[0] + '?rnd=' + data.rnd);
    }, "json");
});

Проверяем на сервере

На сервере в файле check-captcha.php обрабатываем строку. Вся обработка спрятана внутри класса в методе chekSession( )

<?php
require_once($_SERVER['DOCUMENT_ROOT'] . '/wp-load.php'); // API WP
$res = false;
$rnd = md5(uniqid(rand(), true));
if (class_exists('JBlogCaptcha')) {
    $c = JBlogCaptcha::instance();
    $c->chekSession();
    if ($c->getChek()) {

        /*
         * Капча введена верно
         */

        $res = true;

        /*
         * делаем действия на сервере
         * например авторизуем, или еще что то
         */

    }
    /*
     * Так перезагружаем картинку:
     * */
    $c->generateImage('captcha', $c->getSens());
}
$arr = array(
    'rnd' => $rnd,
    'res' => $res
);
print json_encode($arr);

Код плагина:

register_activation_hook(__FILE__, array('JBlogCaptcha', 'activation'));
register_deactivation_hook(__FILE__, array('JBlogCaptcha', 'deactivation'));
register_uninstall_hook(__FILE__, array('JBlogCaptcha', 'uninstall'));
add_action('plugins_loaded', array('JBlogCaptcha', 'instance'));

if (!class_exists('JBlogCaptcha')) {
    class JBlogCaptcha
    {
        const JBC = 'JBlogCaptcha';
        public $plugin_name = '';
        public $plugin_dir = '';
        public $plugin_url = '';
        protected $data = array();

        protected static $_instance = null;


        /*
         * Singleton Pattern
         * */
        public static function instance()
        {
            if (self::$_instance == null) {
                self::$_instance = new JBlogCaptcha();
            }
            return self::$_instance;
        }

        /*
         * Constructor
         * */
        private function __construct()
        {
            /* установка параметров плагина */
            $this->plugin_name = plugin_basename(__FILE__);
            $this->plugin_dir = plugin_dir_path(__FILE__);
            $this->plugin_url = plugin_dir_url(__FILE__);
            /* подгружаем цвет шрифта */
            $this->data['color'] = get_option(self::JBC . '_color'); // array
            /* сообщения в админке */
            $this->data['messSettings'] = '';
            /* сообщение */
            $this->data['mess'] = '';
            /* верно ли введена капча? */
            $this->data['chek'] = get_option(self::JBC . '_is');
            $this->data['token'] = get_option(self::JBC . '_token');

            if (function_exists('add_shortcode')) {
                add_shortcode('jbcptch', array(&$this, 'clientFormShortcode'));
            }

            if (is_admin())
                $this->admin_init();
        }

        private function __clone()
        {
        }

        /*
         * Access methods
         * */
        function getColorRed()
        {
            return $this->data['color']['red'];
        }

        function getColorGreen()
        {
            return $this->data['color']['green'];
        }

        function getColorBlue()
        {
            return $this->data['color']['blue'];
        }

        function getSysMess()
        {
            return $this->data['messSettings'];
        }

        function getChek()
        {
            return $this->data['chek']['is'];
        }

        function getToken(){
            if (get_option(self::JBC . '_token')) {
                $this->data['token'] = get_option(self::JBC . '_token');
            } else {
                $this->data['token'] = '';
            }
            return $this->data['token'];
        }

        function refresh()
        {
            $this->data['chek']['is'] = false;
            $this->data['chek']['sess'] = '';
            update_option(self::JBC . '_is', $this->data['chek']);
        }

        /* Get background CAPTCHA
         * -------------
         * Получить фон капчи, по умолчанию «Рябь»
         * */
        function getBg()
        {
            if (get_option(self::JBC . '_bg')) {
                $this->data['bg'] = get_option(self::JBC . '_bg');
            } else {
                $this->data['bg'] = 'bg';
            }
            return $this->data['bg'];
        }

        /* Get num char of CAPTCHA
         * -------------
         * Получить кол-во символов в капче, по умолчанию 5
         * */
        function getNum()
        {
            if (get_option(self::JBC . '_num')) {
                $this->data['num'] = get_option(self::JBC . '_num');
            } else {
                $this->data['num'] = 5;
            }
            return $this->data['num'];
        }

        /* Get registr
         * -------------
         * Получить опцию регистра, по умолчанию - регистрозависимый
         * */
        function getSens()
        {
            if (get_option(self::JBC . '_sens')) {
                $this->data['sens'] = get_option(self::JBC . '_sens');
            } else {
                $this->data['sens'] = 1;
            }
            return $this->data['sens'];
        }

        /*
         * Generate random string
         * */
        function getRndString($n)
        {
            $base = array('A','a','B','b','C','c','D','d','E','e','F','f','G','g','H','h','J','j','K','k','M','m','N','n','P','p','q','R','r','S','s','T','t','U','u','V','v','W','w','X','x','Y','y','Z','z','2','3','4','5','6','7','8','9');
            return str_replace(array('1','0','l','I','o','O'),'',substr(md5(substr(md5(time() / sqrt(rand(1, 9999))), rand(0, 5), 10)), rand(0, 5), $n-3)).$base[rand(0,20)].$base[rand(21,41)].$base[rand(41,61)];
        }

        /* compare value in this session and value user
        ------------------
         * Проверка капчи
         * Осуществляет проверку верно ли введена капча или нет
         *    Если да - обновляем директиву is в значение true
         *    Если нет - обновляем директиву is в значение false
         * */
        function chekSession()
        {
            ob_start();
            session_start();
            $this->refresh();
            if ($_SERVER["REQUEST_METHOD"] == "POST") {
                if (!$_SESSION['str']) {
                    if($this->getToken() == ''){
                        $this->data["mess"] = 'Включите поддержку картинок в браузере';
                    } else {
                        $cpt = strip_tags(trim($_POST['str']));
                        if($this->getToken() == $cpt){
                            $this->data["mess"] = 'Капча введена верно';
                            $this->data['chek']['is'] = true;
                            $this->data['chek']['sess'] = $cpt;
                            update_option(self::JBC . '_is', $this->data['chek']);
                        }
                    }
                } else {
                    $cpt = strip_tags(trim($_POST['str']));
                    if ($_SESSION['str'] == $cpt) {
                        $this->data["mess"] = 'Капча введена верно';
                        $this->data['chek']['is'] = true;
                        $this->data['chek']['sess'] = $cpt;
                        update_option(self::JBC . '_is', $this->data['chek']);
                    } else {
                        $this->data["mess"] = 'Капча введена неверно';
                        $this->data['chek']['is'] = false;
                        $this->data['chek']['sess'] = $cpt;
                        update_option(self::JBC . '_is', $this->data['chek']);
                    }
                }
            }
            $out = ob_get_clean();
            return $out;
        }

        function chekSessionAdmin(){

            if ($_SERVER["REQUEST_METHOD"] == "POST") {

                $admin = self::instance();
                if(!session_id()) @session_start();

                if(isset($_POST["log"]) && isset($_POST["pwd"]) && !empty($_POST["log"]) && !empty($_POST["pwd"])){
                    if (!$_SESSION['str']) {
                        if($admin->getToken() == ''){
                            $admin->data["mess"] = 'Включите поддержку картинок в браузере';
                            wp_logout();
                            return false;
                        } else {
                            $cpt = strip_tags(trim($_POST['str']));
                            if($admin->getToken() == $cpt){
                                $admin->data["mess"] = 'Капча введена верно';
                                $admin->data['chek']['is'] = true;
                                $admin->data['chek']['sess'] = $cpt;
                                update_option(self::JBC . '_is', $admin->data['chek']);
                                return true;
                            }
                        }
                    } else {
                        $cpt = strip_tags(trim($_POST['str']));
                        if ($_SESSION['str'] == $cpt) {
                            $admin->data["mess"] = 'Капча введена верно';
                            $admin->data['chek']['is'] = true;
                            $admin->data['chek']['sess'] = $cpt;
                            update_option(self::JBC . '_is', $admin->data['chek']);
                            return true;
                        } else {
                            $admin->data["mess"] = 'Капча введена неверно';
                            $admin->data['chek']['is'] = false;
                            $admin->data['chek']['sess'] = $cpt;
                            update_option(self::JBC . '_is', $admin->data['chek']);
                            $admin->generateImage('captcha', $admin->getSens());
                            wp_logout();
                            return false;
                        }
                    }
                } else {
                    return;
                }
            }
            wp_logout();
            return false;
        }

        /*
         *    echo do_shortcode("[jbcptch]") - shortcode - display captcha
         *
         * */
        function clientFormShortcode()
        {
            ob_start();
            ?>
            <div id="jbcptcha_div">
                <div id="generate-jbcptcha">
                    <?php /* Генератор картинки */ ?>
                    <?php $generate = $this->generateImage('captcha', $this->getSens());
                    if ($generate) {
                        ?>
                        <img src="<?php echo $this->plugin_url; ?>assets/img/<?php echo $generate; ?>.jpg?rnd=<?php echo md5(uniqid(rand(), true)); ?>">
                    <?php
                    }else{
                        echo "Произошла ошибка при загрузке изображения..";
                    }
                    ?>
                </div>
                <br/>
                <div id="jbcptcha_settings">
                    <label>Введите символы:</label>
                    <input id="jbcptcha_input" type="text" name="str" size="8" maxlength="5" autocomplete="off">
                    <input type="hidden" name="active_captcha" value="true"/>
                </div>
                <div id="jbcptcha_mess"><?php echo $this->data["mess"] ?></div>
            </div>
            <?php
            $out = ob_get_clean();
            return $out;
        }

        function clientFormShortcodeAdmin(){
            $admin = self::instance();
            $path = $admin->plugin_url;
            $mess = $admin->data["mess"];
            $generate = $admin->generateImage('captcha', $admin->getSens());
            if($generate){
                echo '
                    <div id="jbcptcha_div">
                        <div id="generate-jbcptcha">
                            <img src='.$path.'assets/img/'.$generate.'.jpg?rnd='.md5(uniqid(rand(), true)).'>
                        </div>
                        <br/>
                        <div id="jbcptcha_settings">
                            <label>Введите символы:</label>
                            <input id="jbcptcha_input" type="text" name="str" size="8" maxlength="5" autocomplete="off">
                            <input type="hidden" name="active_captcha" value="true"/>
                        </div>
                        <div id="jbcptcha_mess">'.$mess.'</div>
                    </div>';
            }else{
                echo "Произошла ошибка при загрузке изображения..";
            }
        }

        /*
         * Generate and save img
         * */
        function generateImage($string, $sens)
        {
            @session_start();
            $c = $this->getNum();
            $bg = $this->getBg();
            $i = $this->LoadJpeg($this->plugin_dir . 'assets/img/' . $bg . '.jpg');
            $color = $this->setColor($i, $this->getColorRed(), $this->getColorGreen(), $this->getColorBlue());
            $str = $this->getRndString($c);
            if($sens == 2)
                $str = strtolower($str);
            $_SESSION['str'] = $str;
            update_option(self::JBC . '_token', $str);
            $x = 20;
            $y = 30;
            for ($j = 0; $j < $c; $j++) {
                $offsetX = rand(30, 40);
                $size = rand(20, 30);
                $angle = -25 + rand(0, 50);
                imagettftext($i, $size, $angle, $x, $y, $color, $this->plugin_dir . 'assets/fonts/bellb.ttf', $str[$j]);
                $x += $offsetX;
            }
            $path = $this->plugin_dir . 'assets/img/'.$string.'.jpg';
            $save = imagejpeg($i, $path, 50);
            $del = imagedestroy($i);
            if ($save && $del)
                return $string;
            return false;
        }

        /*
         * load img
         * */
        function LoadJpeg($imgname)
        {
            $im = @imagecreatefromjpeg($imgname);
            if (!$im) {
                $im = imagecreatetruecolor(210, 39);
                $bgc = imagecolorallocate($im, 255, 255, 255);
                $tc = imagecolorallocate($im, 0, 0, 0);

                imagefilledrectangle($im, 0, 0, 150, 30, $bgc);

                imagestring($im, 1, 5, 5, 'Error load ' . $imgname, $tc);
            }
            return $im;
        }

        /*
         * set color fonts
         * */
        function setColor($im, $r, $g, $b)
        {
            if (!$r && !$g && !$b) {
                $r = 60;
                $g = 60;
                $b = 60;
            }
            $colorFonts = imagecolorallocate($im, $r, $g, $b);
            return $colorFonts;
        }

        /**
         * Admin panel
         */
        function admin_init()
        {
            add_action('admin_menu', array($this, 'admin_menu'));
        }

        function admin_menu()
        {
            add_options_page('Настройки JBlog Captcha', 'JBlog Captcha', 'manage_options', __FILE__, array($this, 'admin_options_page'));
        }

        function marker($flag = 'success')
        {
            if ($flag == 'error') {
                ?>
                <script type='text/javascript'>
                    window.onload = function () {
                        var mess = document.getElementById('jb_mess');
                        mess.className = 'error';
                    }
                </script>
            <?php
            } else {
                ?>
                <script type='text/javascript'>
                    window.onload = function () {
                        var mess = document.getElementById('jb_mess');
                        mess.className = 'updated';
                    }
                </script>
            <?php
            }
        }

        function admin_options_page()
        {
            if (!current_user_can('manage_options'))
                return;

            if ($_SERVER["REQUEST_METHOD"] == "POST") {
                if (isset($_POST['jb_submit'])) {
                    if (isset($_POST['red']) && isset($_POST['green']) && isset($_POST['blue'])) {
                        if (!empty($_POST['red']) || $_POST['red'] >= 0 && !empty($_POST['green']) || $_POST['green'] >= 0 && !empty($_POST['blue']) || $_POST['blue'] >= 0) {
                            $red = (int)trim(strip_tags($_POST['red']));
                            $green = (int)trim(strip_tags($_POST['green']));
                            $blue = (int)trim(strip_tags($_POST['blue']));
                            if ($red >= 0 && $green >= 0 && $blue >= 0) {
                                if ($red > 255 || $green > 255 || $blue > 255) {
                                    $this->marker('error');
                                    $this->data['messSettings'] = 'Какой то из параметров превысил максимальное значение..';
                                } else {
                                    $this->data['messSettings'] = 'Настройки сохранены';
                                    $this->marker();
                                    $this->data['color']['red'] = $red;
                                    $this->data['color']['green'] = $green;
                                    $this->data['color']['blue'] = $blue;
                                    update_option(self::JBC . '_color', $this->data['color']);
                                }
                            } else {
                                $this->marker('error');
                                $this->data['messSettings'] = 'Какой то из параметров содержит недопустимые значения..';
                            }
                        } else {
                            $this->marker('error');
                            $this->data['messSettings'] = 'Вы указали не все параметры..';
                        }
                    }
                    if (isset($_POST['getbg']) && "" != $_POST['getbg']) {
                        $this->data['bg'] = $_POST['getbg'];
                        update_option(self::JBC . '_bg', $this->data['bg']);
                    }
                    if (isset($_POST['getnum']) && "" != $_POST['getnum']) {
                        $this->data['num'] = $_POST['getnum'];
                        update_option(self::JBC . '_num', $this->data['num']);
                    }
                    if (isset($_POST['getsens']) && "" != $_POST['getsens']) {
                        $this->data['sens'] = $_POST['getsens'];
                        update_option(self::JBC . '_sens', $this->data['sens']);
                    }
                }
            }
            include $this->plugin_dir . 'include/admin.php';
        }

        /*
         * On activation
         * */
        function activation()
        {
            // при активации
            if (!current_user_can('activate_plugins'))
                return;
        }

        /*
         * On deactivation
         * */
        function deactivation()
        {
            // при деактивации
            if (!current_user_can('activate_plugins'))
                return;
            $plugin = isset($_REQUEST['plugin']) ? $_REQUEST['plugin'] : '';
            check_admin_referer("deactivate-plugin_{$plugin}");

            delete_option(self::JBC);
            delete_option(self::JBC . '_color');
            delete_option(self::JBC . '_bg');
            delete_option(self::JBC . '_is');
            delete_option(self::JBC . '_num');
            delete_option(self::JBC . '_sens');
            delete_option(self::JBC . '_token');
        }

        /*
         * On uninstall
         * */
        function uninstall()
        {
            // при удалении
            if (!current_user_can('activate_plugins'))
                return;
            check_admin_referer('bulk-plugins');
            // Важно: проверим тот ли это файл, который
            // был зарегистрирован во время удаления плагина.
            if (__FILE__ != WP_UNINSTALL_PLUGIN)
                return;
            // проверка пройдена успешно. Начиная от сюда удаляем опции и все остальное.
            delete_option(self::JBC);
            delete_option(self::JBC . '_color');
            delete_option(self::JBC . '_bg');
            delete_option(self::JBC . '_is');
            delete_option(self::JBC . '_num');
            delete_option(self::JBC . '_sens');
            delete_option(self::JBC . '_token');
        }
    }
    add_action('login_form', array('JBlogCaptcha','clientFormShortcodeAdmin'), 1);
    add_action('wp_login', array('JBlogCaptcha','chekSessionAdmin'));
}


Похожие заметки:

Ajax контакт форма на WordPress

Создаем AJAX форму обратной связи на WordPress c использованием капчи jblog-captcha

Открыть здесь


Перед тем как писать комментарии, рекомендую ознакомиться:

Markdown синтаксис »

Оформление кода »

Нужна аватарка »

Комментарии


2
avatar

Антон Е. сказал 04-04-2017 в 20:14


Роман, у вас в коде плагина шаблон проектирования "одиночки" не совсем корректно прописан.

Новый экземпляр создаётся с помощью позднего статического связывания в статическом методе instance() с ключевым словом static

И еще нужно определеить магический метод __wakeup как частный (private):

Ссылка ->


avatar

Админ

Роман Жариков сказал 04-04-2017 в 20:20

   В ответ на комментарии автора Антон Е.

Спасибо, Антон за отличное замечание! Я постараюсь внести новые изменения в репозитории как можно скорее )