1<?php
2/**
3 * Zend Framework (http://framework.zend.com/)
4 *
5 * @link      http://github.com/zendframework/zf2 for the canonical source repository
6 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
7 * @license   http://framework.zend.com/license/new-bsd New BSD License
8 */
9
10namespace Zend\Form\View\Helper\Captcha;
11
12use Zend\Captcha\ReCaptcha as CaptchaAdapter;
13use Zend\Form\ElementInterface;
14use Zend\Form\Exception;
15use Zend\Form\View\Helper\FormInput;
16
17class ReCaptcha extends FormInput
18{
19    /**
20     * Invoke helper as functor
21     *
22     * Proxies to {@link render()}.
23     *
24     * @param  ElementInterface $element
25     * @return string
26     */
27    public function __invoke(ElementInterface $element = null)
28    {
29        if (!$element) {
30            return $this;
31        }
32
33        return $this->render($element);
34    }
35
36    /**
37     * Render ReCaptcha form elements
38     *
39     * @param  ElementInterface $element
40     * @throws Exception\DomainException
41     * @return string
42     */
43    public function render(ElementInterface $element)
44    {
45        $attributes = $element->getAttributes();
46        $captcha = $element->getCaptcha();
47
48        if ($captcha === null || !$captcha instanceof CaptchaAdapter) {
49            throw new Exception\DomainException(sprintf(
50                '%s requires that the element has a "captcha" attribute implementing Zend\Captcha\AdapterInterface; none found',
51                __METHOD__
52            ));
53        }
54
55        $name          = $element->getName();
56        $id            = isset($attributes['id']) ? $attributes['id'] : $name;
57        $challengeName = empty($name) ? 'recaptcha_challenge_field' : $name . '[recaptcha_challenge_field]';
58        $responseName  = empty($name) ? 'recaptcha_response_field'  : $name . '[recaptcha_response_field]';
59        $challengeId   = $id . '-challenge';
60        $responseId    = $id . '-response';
61
62        $markup = $captcha->getService()->getHtml($name);
63        $hidden = $this->renderHiddenInput($challengeName, $challengeId, $responseName, $responseId);
64        $js     = $this->renderJsEvents($challengeId, $responseId);
65
66        return $hidden . $markup . $js;
67    }
68
69    /**
70     * Render hidden input elements for the challenge and response
71     *
72     * @param  string $challengeName
73     * @param  string $challengeId
74     * @param  string $responseName
75     * @param  string $responseId
76     * @return string
77     */
78    protected function renderHiddenInput($challengeName, $challengeId, $responseName, $responseId)
79    {
80        $pattern        = '<input type="hidden" %s%s';
81        $closingBracket = $this->getInlineClosingBracket();
82
83        $attributes = $this->createAttributesString(array(
84            'name' => $challengeName,
85            'id'   => $challengeId,
86        ));
87        $challenge = sprintf($pattern, $attributes, $closingBracket);
88
89        $attributes = $this->createAttributesString(array(
90            'name' => $responseName,
91            'id'   => $responseId,
92        ));
93        $response = sprintf($pattern, $attributes, $closingBracket);
94
95        return $challenge . $response;
96    }
97
98    /**
99     * Create the JS events used to bind the challenge and response values to the submitted form.
100     *
101     * @param  string $challengeId
102     * @param  string $responseId
103     * @return string
104     */
105    protected function renderJsEvents($challengeId, $responseId)
106    {
107        $elseif = 'else if'; // php-cs-fixer bug
108        $js =<<<EOJ
109<script type="text/javascript" language="JavaScript">
110function windowOnLoad(fn)
111{
112    var old = window.onload;
113    window.onload = function () {
114        if (old) {
115            old();
116        }
117        fn();
118    };
119}
120function zendBindEvent(el, eventName, eventHandler)
121{
122    if (el.addEventListener) {
123        el.addEventListener(eventName, eventHandler, false);
124    } $elseif (el.attachEvent) {
125        el.attachEvent('on'+eventName, eventHandler);
126    }
127}
128windowOnLoad(function () {
129    zendBindEvent(
130        document.getElementById("$challengeId").form,
131        'submit',
132        function (e) {
133            document.getElementById("$challengeId").value = document.getElementById("recaptcha_challenge_field").value;
134            document.getElementById("$responseId").value = document.getElementById("recaptcha_response_field").value;
135        }
136    );
137});
138</script>
139EOJ;
140        return $js;
141    }
142}
143