1:   2:   3:   4:   5:   6:   7:   8:   9:  10:  11:  12:  13:  14:  15:  16:  17:  18:  19:  20:  21:  22:  23:  24:  25:  26:  27:  28:  29:  30:  31:  32:  33:  34:  35:  36:  37:  38:  39:  40:  41:  42:  43:  44:  45:  46:  47:  48:  49:  50:  51:  52:  53:  54:  55:  56:  57:  58:  59:  60:  61:  62:  63:  64:  65:  66:  67:  68:  69:  70:  71:  72:  73:  74:  75:  76:  77:  78:  79:  80:  81:  82:  83:  84:  85:  86:  87:  88:  89:  90:  91:  92:  93:  94:  95:  96:  97:  98:  99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 
<?php
namespace Omeka\Form\Element;

use Zend\Form\Element;
use Zend\Http\Client;
use Zend\InputFilter\InputProviderInterface;

/**
 * A reCAPTCHA form element used to verify whether a user is human.
 */
class Recaptcha extends Element implements InputProviderInterface
{
    /**
     * @var array
     */
    protected $attributes = [
        'type' => 'recaptcha',
        'name' => 'g-recaptcha-response',
        'class' => 'g-recaptcha',
    ];

    /**
     * @var string The reCAPTCHA site key
     */
    protected $siteKey;

    /**
     * @var string The reCAPTCHA secret key
     */
    protected $secretKey;

    /**
     * @var string The remote IP address
     */
    protected $remoteIp;

    /**
     * @var Client The HTTP client, configured for SSL
     */
    protected $client;

    public function __construct($name = null, $options = [])
    {
        parent::__construct($name, array_merge($this->options, $options));
    }

    public function setOptions($options)
    {
        parent::setOptions($options);

        if (isset($this->options['site_key'])) {
            $this->setSiteKey($this->options['site_key']);
        }
        if (isset($this->options['secret_key'])) {
            $this->setSecretKey($this->options['secret_key']);
        }
        if (isset($this->options['remote_ip'])) {
            $this->setRemoteIp($this->options['remote_ip']);
        }

        return $this;
    }

    public function setSiteKey($siteKey)
    {
        $this->siteKey = $siteKey;
        $this->setAttribute('data-sitekey', $siteKey);
        return $this;
    }

    public function setSecretKey($secretKey)
    {
        $this->secretKey = $secretKey;
        return $this;
    }

    public function setRemoteIp($remoteIp)
    {
        $this->remoteIp = $remoteIp;
        return $this;
    }

    public function setClient(Client $client)
    {
        $this->client = $client;
        return $this;
    }

    public function getInputSpecification()
    {
        return [
            'name' => 'g-recaptcha-response',
            'required' => true,
            'validators' => [
                [
                    'name' => 'NotEmpty',
                    'options' => [
                        'messages' => [
                            'isEmpty' => 'You must verify that you are human by completing the CAPTCHA.', // @translate
                        ],
                    ],
                ],
                [
                    'name' => 'Callback',
                    'options' => [
                        'callback' => [$this, 'isValid'],
                        'messages' => [
                            'callbackValue' => 'Could not verify that you are a human.', // @translate
                        ],
                    ],
                ],
            ],
        ];
    }

    /**
     * Validate the reCAPTCHA.
     *
     * @param string $value
     * @return bool
     */
    public function isValid($value)
    {
        $response = $this->client
            ->setUri('https://www.google.com/recaptcha/api/siteverify')
            ->setMethod('POST')
            ->setParameterPost([
                'response' => $value,
                'secret' => $this->secretKey,
                'remoteip' => $this->remoteIp,
            ])->send();
        $apiResponse = json_decode($response->getBody(), true);
        if ($apiResponse['success']) {
            return true;
        }
        return false;
    }
}