Index: /main/waeup.kofa/trunk/src/waeup/kofa/browser/captcha.py
===================================================================
--- /main/waeup.kofa/trunk/src/waeup/kofa/browser/captcha.py	(revision 15011)
+++ /main/waeup.kofa/trunk/src/waeup/kofa/browser/captcha.py	(revision 15012)
@@ -21,4 +21,5 @@
 """
 import grok
+import json
 import urllib
 import urllib2
@@ -163,14 +164,11 @@
 
 ##
-## ReCaptcha
-##
-API_SSL_SERVER = "https://www.google.com/recaptcha/api"
-VERIFY_SERVER = "https://www.google.com/recaptcha/api"
-
+## ReCaptcha (v2)
+##
 class ReCaptcha(StaticCaptcha):
     """ReCaptcha - strong captchas with images, sound, etc.
 
     This is the Kofa implementation to support captchas as provided by
-    http://www.google.com/recaptcha.
+    http://www.google.com/recaptcha (v2).
 
     ReCaptcha is widely used and adopted in web applications. See the
@@ -220,12 +218,17 @@
     This captcha is available at runtime as a global utility named
     ``'ReCaptcha'``.
+
+    Shortcomings:
+        - no support for selecting theme
+        - no support for selecting box layout (normal, compact)
+        - no support for non-javascript users
+        - Google will get the data of our clients
+        - Google will track all activity of our clients
     """
 
     grok.implements(ICaptcha)
 
-    #: name of solution field in HTTP request
-    sol_field = 'recaptcha_response_field'
-    #: name of challenge field in HTTP request
-    chal_field = 'recaptcha_challenge_field'
+    #: name of response token field in HTTP request
+    token_field = 'g-recaptcha-response'
 
     # Do not use the following keys in productive environments!  As
@@ -235,9 +238,11 @@
     PRIVATE_KEY = "6Lc0y8oSAAAAAMHVbMrGWLLjw2pm8v2Uprwm9AbR"
 
+    VERIFY_URL = 'https://www.google.com/recaptcha/api/siteverify'
+
     def verify(self, request):
         """Grab challenge/solution from HTTP request and verify it.
 
         Verification happens against recaptcha remote API servers. It
-        only happens, when really a solution was sent with the
+        only happens, when a repsonse token was sent with the
         request.
 
@@ -246,21 +251,16 @@
         """
         form = getattr(request, 'form', {})
-        solution=form.get(self.sol_field, None)
-        challenge=form.get(self.chal_field, None)
-        if not challenge or not solution:
-            # Might be first-time display of the captcha: not a valid
-            # solution but no error code to prevent any error
-            # messages. Skip further verification.
-            return CaptchaResponse(
-                is_valid=False)
+        token = form.get(self.token_field, None)
+        if not token:
+            # on first-time display, we won't get a token
+            return CaptchaResponse(is_valid=False)
         params = urllib.urlencode(
             {
-                'privatekey': self.PRIVATE_KEY,
+                'secret': self.PRIVATE_KEY,
+                'response': token,
                 'remoteip': '127.0.0.1',
-                'challenge': challenge,
-                'response': solution,
                 })
         request = urllib2.Request(
-            url = "%s/verify" % VERIFY_SERVER,
+            url = self.VERIFY_URL,
             data = params,
             headers = {
@@ -269,15 +269,14 @@
                 }
             )
-        resp = urllib2.urlopen(request)
-        ret_vals = resp.read().splitlines()
-        resp.close()
-        ret_code, err_code = ret_vals
-
-        if ret_code == "true":
+        conn = urllib2.urlopen(request)
+        ret_vals = json.loads(conn.read())
+        conn.close()
+        if ret_vals.get('success', False) is True:
             return CaptchaResponse(is_valid=True)
-        return CaptchaResponse(is_valid=False, error_code=ret_vals[1])
+        return CaptchaResponse(
+            is_valid=False, error_code="%s" % ret_vals['error-codes'])
 
     def display(self, error_code=None):
-        """Display challenge and input field for solution as HTML.
+        """Display captcha widget snippet.
 
         Returns the HTML code to be placed inside an existing ``<form>``
@@ -297,22 +296,13 @@
         html = (
             u'<script type="text/javascript" '
-            u'src="%(ApiServer)s/challenge?k=%(PublicKey)s%(ErrorParam)s">'
+            u'src="%(ApiServer)s" async defer>'
             u'</script>'
-            u''
-            u'<noscript>'
-            u'<iframe'
-            u'    src="%(ApiServer)s/noscript?k=%(PublicKey)s%(ErrorParam)s"'
-            u'    height="300" width="500" frameborder="0"></iframe><br />'
-            u'<textarea name="recaptcha_challenge_field"'
-            u'          rows="3" cols="40"></textarea>'
-            u'<input type="hidden" name="recaptcha_response_field"'
-            u'       value="manual_challenge" />'
-            u'</noscript>' % {
-                'ApiServer' : API_SSL_SERVER,
-                'PublicKey' : self.PUBLIC_KEY,
-                'ErrorParam' : error_param,
+            u'<div class="g-recaptcha" data-sitekey="%(SiteKey)s"></div>' % {
+                'ApiServer': "https://www.google.com/recaptcha/api.js",
+                'SiteKey': self.PUBLIC_KEY,
                 }
-            )
+        )
         return html
+
 
 grok.global_utility(ReCaptcha, name=u'ReCaptcha')
Index: /main/waeup.kofa/trunk/src/waeup/kofa/browser/tests/test_captcha.py
===================================================================
--- /main/waeup.kofa/trunk/src/waeup/kofa/browser/tests/test_captcha.py	(revision 15011)
+++ /main/waeup.kofa/trunk/src/waeup/kofa/browser/tests/test_captcha.py	(revision 15012)
@@ -171,12 +171,7 @@
         result = captcha.display()
         self.assertMatches(
-            '<script type="text/javascript" src="..."></script>'
-            '<noscript>'
-            '<iframe src="..." height="300" width="500" '
-            '        frameborder="0"></iframe><br />'
-            '<textarea name="recaptcha_challenge_field" rows="3" '
-            '          cols="40"></textarea>'
-            '<input type="hidden" name="recaptcha_response_field" '
-            '       value="manual_challenge" /></noscript>',
+            '<script type="text/javascript" '
+            'src="..." async defer></script>'
+            '<div class="g-recaptcha" data-sitekey="..."></div>',
             result)
         return
