| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751 |
- <?php
- /**
- * @file
- * Fake Mollom REST Testing server implementation.
- *
- * This is a simplified re-implementation of Mollom's REST Testing API, which
- * is used by functional Drupal module tests only.
- *
- * While the duplication means additional engineering work, the fake server
- * provides unique testing facilities that are not possible to achieve with
- * Mollom's official REST Testing API server:
- *
- * - All requests and request parameters are stored, which enables tests to
- * retrieve previously sent request information and assert that the client
- * sent the expected parameters and values (and no unexpected parameters).
- * This is important since most of the business logic for request parameters
- * depends on external user input and lives in form processing code; i.e.,
- * the Mollom module's primary functionality is not unit-testable.
- * - Special test server variables allow tests to trigger special and unexpected
- * service conditions in between requests, such as 404s or service downtimes,
- * without having to manipulate the regular request parameters (and thus,
- * without having to add testing related code and logic to the regular runtime
- * code).
- * - The module's behavior and communication can be tested against this fake
- * server implementation without having the Testing mode enabled; i.e., this
- * fake server can also act as production server.
- *
- * @see MollomWebTestCase::getServerRecord()
- *
- * In addition, this fake server implementation allows to double-check whether
- * Mollom's API follows the expectations of Mollom client developers, and it
- * successfully helped to discover inconsistencies as well as unexpected API
- * behaviors in the past.
- *
- * The module communicates with this fake server when the 'mollom_class'
- * variable is set to 'MollomDrupalTestLocal'. The fake test server module is
- * automatically enabled for tests when needed.
- *
- * @see MollomWebTestCase::setUp()
- * @see MollomDrupalTestLocal
- *
- * The fake server architecture is kept as simple as possible:
- *
- * - Menu router definitions are registering a controller/handler for each main
- * API endpoint.
- * - The controller callback validates OAuth parameters (when applicable) and
- * optionally calls into dedicated functions to handle the request-specific
- * logic (e.g., Content API) or performs the logic directly (e.g., Site API).
- * - The callback result, i.e. response parameters (or error code), is processed
- * by the delivery callback and transformed into an HTTP response following
- * the Mollom API format.
- *
- * @see mollom_test_server_menu()
- * @see mollom_test_server_rest_deliver()
- *
- * Clean URLs must be enabled.
- */
- /**
- * Implements hook_menu().
- */
- function mollom_test_server_menu() {
- // Base path and argument count for all registered routes.
- $path = 'mollom-test/rest/v1';
- $base_args = count(explode('/', $path)) - 1;
- // @todo Consider to use a generic page callback, passing arg(3), the resource
- // type, and optionally arg(4), the resource, as argument. This would allow
- // us to use PHP Exceptions to throw different status codes and errors. Make
- // that page callback dynamically switch the delivery callback (for JSON).
- $base = array(
- // Access depends on whether OAuth parameters have been sent, and in any
- // case, error responses must be in the requested/accepted Content-Type
- // passed by the client (as opposed to a 403 HTML page generated by Drupal).
- 'access callback' => TRUE,
- 'type' => MENU_CALLBACK,
- 'delivery callback' => 'mollom_test_server_rest_deliver',
- );
- $items[$path . '/site'] = $base + array(
- 'page callback' => 'mollom_test_server_rest_site',
- );
- $items[$path . '/content'] = $base + array(
- 'page callback' => 'mollom_test_server_rest_content',
- );
- $items[$path . '/captcha'] = $base + array(
- 'page callback' => 'mollom_test_server_rest_captcha',
- );
- $items[$path . '/feedback'] = $base + array(
- 'page callback' => 'mollom_test_server_rest_send_feedback',
- );
- $items[$path . '/blacklist/%'] = $base + array(
- 'page callback' => 'mollom_test_server_rest_blacklist',
- 'page arguments' => array($base_args + 2),
- );
- // @todo Whitelist endpoints.
- return $items;
- }
- /**
- * Returns HTTP request query parameters for the current request.
- *
- * @see Mollom::httpBuildQuery()
- * @see http://php.net/manual/en/wrappers.php.php
- */
- function mollom_test_server_rest_get_parameters() {
- $data = &drupal_static(__FUNCTION__);
- if (isset($data)) {
- return $data;
- }
- // @todo Replace with Mollom::getServerParameters()
- if ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD') {
- $data = Mollom::httpParseQuery($_SERVER['QUERY_STRING']);
- // Remove $_GET['q'].
- // @see .htaccess
- unset($data['q']);
- }
- elseif ($_SERVER['REQUEST_METHOD'] == 'POST' || $_SERVER['REQUEST_METHOD'] == 'PUT') {
- $data = Mollom::httpParseQuery(file_get_contents('php://input'));
- }
- return $data;
- }
- /**
- * Returns the parsed HTTP Authorization request header as an array.
- *
- * @todo Replace with Mollom::getServerAuthentication()
- */
- function mollom_test_server_rest_get_auth_header() {
- $header = &drupal_static(__FUNCTION__);
- if (isset($header)) {
- return $header;
- }
- $header = array();
- if (function_exists('apache_request_headers')) {
- $headers = apache_request_headers();
- if (isset($headers['Authorization'])) {
- $input = $headers['Authorization'];
- }
- }
- elseif (isset($_SERVER['HTTP_AUTHORIZATION'])) {
- $input = $_SERVER['HTTP_AUTHORIZATION'];
- }
- if (isset($input)) {
- preg_match_all('@([^, =]+)="([^"]*)"@', $input, $header);
- $header = array_combine($header[1], $header[2]);
- }
- return $header;
- }
- /**
- * Delivery callback for REST API endpoints.
- */
- function mollom_test_server_rest_deliver($page_callback_result) {
- // All fake-server responses are not cached.
- drupal_page_is_cacheable(FALSE);
- drupal_add_http_header('Content-Type', 'application/xml; charset=utf-8');
- $xml = new DOMDocument('1.0', 'utf-8');
- $element = $xml->createElement('response');
- // Append status response parameters.
- // @todo Add support for custom codes (redirect/refresh) + error messages.
- $code = 200;
- if (!is_array($page_callback_result) && $page_callback_result !== TRUE) {
- switch ($page_callback_result) {
- case MENU_NOT_FOUND:
- $code = 404;
- $message = 'Not found';
- break;
- case Mollom::AUTH_ERROR:
- $code = 401;
- $message = 'Unauthorized';
- break;
- default:
- $code = 400;
- $message = 'Bad request';
- break;
- }
- }
- $status = array(
- 'code' => $code,
- );
- if (isset($message)) {
- $status['message'] = $message;
- }
- mollom_test_server_rest_add_xml($xml, $element, $status);
- // Append other response parameters.
- if (is_array($page_callback_result)) {
- mollom_test_server_rest_add_xml($xml, $element, $page_callback_result);
- }
- $xml->appendChild($element);
- print $xml->saveXML();
- // Perform end-of-request tasks.
- drupal_page_footer();
- }
- function mollom_test_server_rest_add_xml(DOMDocument $doc, DOMNode $parent, $data, $key = NULL) {
- if (is_scalar($data)) {
- // Mollom REST API always uses integers instead of Booleans due to varying
- // implementations of JSON protocol across client platforms/frameworks.
- if (is_bool($data)) {
- $data = (int) $data;
- }
- $element = $doc->createTextNode($data);
- $parent->appendChild($element);
- }
- else {
- foreach ($data as $property => $value) {
- $key = (is_numeric($property) ? 'item' : $property);
- $element = $doc->createElement($key);
- $parent->appendChild($element);
- mollom_test_server_rest_add_xml($doc, $element, $value, $key);
- }
- }
- }
- /**
- * Returns whether the OAuth request signature is valid.
- */
- function mollom_test_server_rest_validate_auth() {
- $data = mollom_test_server_rest_get_parameters();
- $header = mollom_test_server_rest_get_auth_header();
- $sites = variable_get('mollom_test_server_site', array());
- // Validate the timestamp.
- $client_time = $header['oauth_timestamp'];
- $time = REQUEST_TIME;
- $offset = abs($time - $client_time);
- if ($offset > Mollom::TIME_OFFSET_MAX) {
- return FALSE;
- }
- $sent_signature = $header['oauth_signature'];
- unset($header['oauth_signature']);
- $base_string = implode('&', array(
- $_SERVER['REQUEST_METHOD'],
- Mollom::rawurlencode($GLOBALS['base_url'] . '/' . $_GET['q']),
- Mollom::rawurlencode(Mollom::httpBuildQuery($data + $header)),
- ));
- $nonce = $header['oauth_nonce'];
- if (!isset($sites[$header['oauth_consumer_key']]['privateKey'])) {
- return FALSE;
- }
- $privateKey = $sites[$header['oauth_consumer_key']]['privateKey'];
- $key = Mollom::rawurlencode($privateKey) . '&' . '';
- $signature = rawurlencode(base64_encode(hash_hmac('sha1', $base_string, $key, TRUE)));
- return $signature === $sent_signature;
- }
- /**
- * REST callback for CRUD site operations.
- *
- * @param $publicKey
- * (optional) The public key of a site.
- * @param $delete
- * (optional) Whether to delete the site with $publicKey.
- */
- function mollom_test_server_rest_site($publicKey = NULL, $delete = FALSE) {
- $data = mollom_test_server_rest_get_parameters();
- $bin = 'mollom_test_server_site';
- $sites = variable_get($bin, array());
- if (isset($publicKey)) {
- // Validate authentication.
- if (!mollom_test_server_rest_validate_auth()) {
- return Mollom::AUTH_ERROR;
- }
- // Check whether publicKey exists.
- if (!isset($sites[$publicKey])) {
- return MENU_NOT_FOUND;
- }
- }
- if ($_SERVER['REQUEST_METHOD'] == 'GET') {
- // Return existing site.
- if (isset($publicKey)) {
- $response = $sites[$publicKey];
- }
- // Return list of existing sites.
- else {
- $response = array(
- 'list' => array_values($sites),
- 'listCount' => count($sites),
- 'listOffset' => 0,
- 'listTotal' => count($sites),
- );
- return $response;
- }
- }
- else {
- // Update site.
- if (isset($publicKey) && !$delete) {
- $sites[$publicKey] = $data + $sites[$publicKey];
- variable_set($bin, $sites);
- $response = $sites[$publicKey];
- }
- // Create new site.
- // Authentication is ignored in this case.
- elseif (!$delete) {
- $data['publicKey'] = $publicKey = md5(rand() . REQUEST_TIME);
- $data['privateKey'] = $privateKey = md5(rand() . REQUEST_TIME);
- // Apply default values.
- $data += array(
- 'url' => '',
- 'email' => '',
- 'expectedLanguages' => array(),
- 'subscriptionType' => '',
- // Client version info is not defined by default.
- );
- $sites[$publicKey] = $data;
- variable_set($bin, $sites);
- $response = $data;
- }
- // Delete site.
- else {
- unset($sites[$publicKey]);
- variable_set($bin, $sites);
- return TRUE;
- }
- }
- return array('site' => $response);
- }
- /**
- * REST callback for mollom.checkContent to perform textual analysis.
- */
- function mollom_test_server_rest_content($contentId = NULL) {
- $data = mollom_test_server_rest_get_parameters();
- if ($_SERVER['REQUEST_METHOD'] == 'GET') {
- // @todo List/read content.
- if (empty($contentId)) {
- return FALSE;
- }
- return FALSE;
- }
- else {
- // Content ID in request parameters must match the one in path.
- if (isset($data['id']) && $data['id'] != $contentId) {
- return FALSE;
- }
- if (isset($contentId)) {
- $data['id'] = $contentId;
- }
- }
- // Default POST: Create or update content and check it.
- return array('content' => mollom_test_server_check_content($data));
- }
- /**
- * REST callback to for CAPTCHAs.
- */
- function mollom_test_server_rest_captcha($captchaId = NULL) {
- $data = mollom_test_server_rest_get_parameters();
- if ($_SERVER['REQUEST_METHOD'] == 'GET') {
- // There is no GET /captcha[/{captchaId}].
- return FALSE;
- }
- else {
- // CAPTCHA ID in request parameters must match the one in path.
- if (isset($data['id']) && $data['id'] != $captchaId) {
- return FALSE;
- }
- // Verify CAPTCHA.
- if (isset($captchaId)) {
- $data['id'] = $captchaId;
- $response = mollom_test_server_check_captcha($data);
- if (!is_array($response)) {
- return $response;
- }
- return array('captcha' => $response);
- }
- }
- // Create a new CAPTCHA resource.
- return array('captcha' => mollom_test_server_get_captcha($data));
- }
- /**
- * REST callback for Blacklist API.
- *
- * @param $public_key
- * The public key of a site.
- *
- * @todo Abstract actual functionality like other REST handlers.
- */
- function mollom_test_server_rest_blacklist($public_key, $entryId = NULL, $delete = FALSE) {
- if (empty($public_key)) {
- return FALSE;
- }
- $data = mollom_test_server_rest_get_parameters();
- // Prepare text value.
- if (isset($data['value'])) {
- $data['value'] = drupal_strtolower(trim($data['value']));
- }
- $bin = 'mollom_test_server_blacklist_' . $public_key;
- $entries = variable_get($bin, array());
- if ($_SERVER['REQUEST_METHOD'] == 'GET') {
- // List blacklist entries.
- if (empty($entryId)) {
- $response = array();
- // Remove deleted entries (== FALSE).
- $entries = array_filter($entries);
- $response['list'] = $entries;
- // @todo Not required yet.
- $response['listCount'] = count($entries);
- $response['listOffset'] = 0;
- $response['listTotal'] = count($entries);
- return $response;
- }
- // Read a single entry.
- else {
- // Check whether the entry exists and was not deleted.
- if (!empty($entries[$entryId])) {
- return array('entry' => $entries[$entryId]);
- }
- else {
- return MENU_NOT_FOUND;
- }
- }
- }
- else {
- // Update an existing entry.
- if (isset($entryId)) {
- // Entry ID must match.
- if (isset($data['id']) && $data['id'] != $entryId) {
- return FALSE;
- }
- // Check that the entry was not deleted.
- if (empty($entries[$entryId])) {
- return MENU_NOT_FOUND;
- }
- // Entry ID cannot be updated.
- unset($data['id']);
- $entries[$entryId] = $data;
- variable_set($bin, $entries);
- $response = $data;
- $response['id'] = $entryId;
- return array('entry' => $response);
- }
- // Create a new entry.
- elseif (!$delete) {
- $entryId = max(array_keys($entries)) + 1;
- $data['id'] = $entryId;
- $entries[$entryId] = $data;
- variable_set($bin, $entries);
- $response = $data;
- return array('entry' => $response);
- }
- // Delete an existing entry.
- else {
- // Check that the entry was not deleted already.
- if (!empty($entries[$entryId])) {
- $entries[$entryId] = FALSE;
- variable_set($bin, $entries);
- return TRUE;
- }
- else {
- return MENU_NOT_FOUND;
- }
- }
- }
- }
- /**
- * REST callback for mollom.sendFeedback to send feedback for a moderated post.
- */
- function mollom_test_server_rest_send_feedback() {
- $data = mollom_test_server_rest_get_parameters();
- // A resource ID is required.
- if (empty($data['contentId']) && empty($data['captchaId'])) {
- return 400;
- }
- // The feedback is valid if the supplied reason is one of the supported
- // strings. Otherwise, it's a bad request.
- $storage = variable_get('mollom_test_server_feedback', array());
- $storage[] = $data;
- variable_set('mollom_test_server_feedback', $storage);
- // Default value assumed in the API for feedback type is "moderate".
- if (empty($data['type'])) {
- $data['type'] = 'moderate';
- }
- $reason_result = in_array($data['reason'], array('spam', 'profanity', 'quality', 'unwanted', 'approve', 'delete'));
- $feedback_result = in_array($data['type'], array('flag', 'moderate'));
- return $reason_result && $feedback_result ? TRUE : 400;
- }
- /**
- * API callback for mollom.checkContent to perform textual analysis.
- *
- * @todo Add support for 'redirect' and 'refresh' values.
- */
- function mollom_test_server_check_content($data) {
- $response = array();
- // If only a single value for checks is passed, it is a string.
- if (isset($data['checks']) && is_string($data['checks'])) {
- $data['checks'] = array($data['checks']);
- }
- $header = mollom_test_server_rest_get_auth_header();
- $publicKey = $header['oauth_consumer_key'];
- // Fetch blacklist.
- $blacklist = variable_get('mollom_test_server_blacklist_' . $publicKey, array());
- // Determine content keys to analyze.
- $post_keys = array('postTitle' => 1, 'postBody' => 1);
- $type = FALSE;
- if (isset($data['type']) && in_array($data['type'], array('user'))) {
- $type = $data['type'];
- if ($type == 'user') {
- $post_keys += array('authorName' => 1, 'authorMail' => 1);
- }
- }
- $post = implode('\n', array_intersect_key($data, $post_keys));
- $update = isset($data['stored']);
- // Spam filter: Check post_title and post_body for ham, spam, or unsure.
- if (!$update && (!isset($data['checks']) || in_array('spam', $data['checks']))) {
- $spam = FALSE;
- $ham = FALSE;
- // 'spam' always has precedence.
- if (strpos($post, 'spam') !== FALSE) {
- $spam = TRUE;
- }
- // Otherwise, check for 'ham'.
- elseif (strpos($post, 'ham') !== FALSE) {
- $ham = TRUE;
- }
- // Lastly, take a forced 'unsure' into account.
- elseif (strpos($post, 'unsure') !== FALSE) {
- // Enabled unsure mode.
- if (!isset($data['unsure']) || $data['unsure']) {
- $spam = TRUE;
- $ham = TRUE;
- }
- // Binary mode.
- else {
- $spam = FALSE;
- $ham = TRUE;
- }
- }
- // Check blacklist.
- if ($matches = mollom_test_server_check_content_blacklist($post, $blacklist, 'spam')) {
- $spam = TRUE;
- $ham = FALSE;
- $response['reason'] = 'blacklist';
- $response['blacklistSpam'] = $matches;
- }
- if ($spam && $ham) {
- $response['spamScore'] = 0.5;
- $response['spamClassification'] = 'unsure';
- $qualityScore = 0.5;
- }
- elseif ($spam) {
- $response['spamScore'] = 1.0;
- $response['spamClassification'] = 'spam';
- $qualityScore = 0.0;
- }
- elseif ($ham) {
- $response['spamScore'] = 0.0;
- $response['spamClassification'] = 'ham';
- $qualityScore = 1.0;
- }
- else {
- $response['spamScore'] = 0.5;
- $response['spamClassification'] = 'unsure';
- $qualityScore = NULL;
- }
- // In case a previous spam check was unsure and a CAPTCHA was solved, the
- // result is supposed to be ham - unless the new content is spam.
- if (!empty($data['id']) && $response['spamClassification'] == 'unsure') {
- $content_captchas = variable_get('mollom_test_server_content_captcha', array());
- if (!empty($content_captchas[$data['id']])) {
- $response['spamScore'] = 0.0;
- $response['spamClassification'] = 'ham';
- }
- }
- }
- // Quality filter.
- if (isset($data['checks']) && in_array('quality', $data['checks'])) {
- if (isset($qualityScore)) {
- $response['qualityScore'] = $qualityScore;
- }
- else {
- $response['qualityScore'] = 0;
- }
- }
- // Profanity filter.
- if (isset($data['checks']) && in_array('profanity', $data['checks'])) {
- $profanityScore = 0.0;
- if (strpos($post, 'profanity') !== FALSE) {
- $profanityScore = 1.0;
- }
- // Check blacklist.
- if ($matches = mollom_test_server_check_content_blacklist($post, $blacklist, 'profanity')) {
- $profanityScore = 1.0;
- $response['blacklistProfanity'] = $matches;
- }
- $response['profanityScore'] = $profanityScore;
- }
- // Language detection.
- if (isset($data['checks']) && in_array('language', $data['checks'])) {
- $languages = array();
- if (stripos($post, 'ist seit der Mitte')) {
- $languages[] = array(
- 'languageCode' => 'de',
- );
- }
- if (stripos($post, 'it is the most populous city')) {
- $languages[] = array(
- 'languageCode' => 'en',
- );
- }
- if (count($languages) == 0) {
- $languages[] = array(
- 'languageCode' => 'zxx',
- );
- }
- $score = 1/count($languages);
- foreach($languages as $id => $langObj) {
- $languages[$id]['languageScore'] = $score;
- }
- $response['languages'] = $languages;
- }
- $storage = variable_get('mollom_test_server_content', array());
- $contentId = (!empty($data['id']) ? $data['id'] : md5(mt_rand()));
- if (isset($storage[$contentId])) {
- $storage[$contentId] = array_merge($storage[$contentId], $data);
- }
- else {
- $storage[$contentId] = $data;
- }
- if ($update) {
- $response = array_merge($storage[$contentId], $response);
- }
- $response['id'] = $contentId;
- variable_set('mollom_test_server_content', $storage);
- return $response;
- }
- /**
- * Checks a string against blacklisted terms.
- */
- function mollom_test_server_check_content_blacklist($string, $blacklist, $reason) {
- $terms = array();
- foreach ($blacklist as $entry) {
- if ($entry['reason'] == $reason) {
- $term = preg_quote($entry['value']);
- if ($entry['match'] == 'exact') {
- $term = '\b' . $term . '\b';
- }
- $terms[] = $term;
- }
- }
- if (!empty($terms)) {
- $terms = '/(' . implode('|', $terms) . ')/';
- preg_match_all($terms, strtolower($string), $matches);
- return $matches[1];
- }
- return array();
- }
- /**
- * API callback for mollom.getImageCaptcha to fetch a CAPTCHA image.
- */
- function mollom_test_server_get_captcha($data) {
- $captchaId = (!empty($data['id']) ? $data['id'] : md5(mt_rand()));
- $response = array(
- 'id' => $captchaId,
- );
- // Return a HTTPS URL if 'ssl' parameter was passed.
- $base_url = $GLOBALS['base_url'];
- if (!empty($data['ssl'])) {
- $base_url = str_replace('http', 'https', $base_url);
- }
- $response['url'] = $base_url . '/' . drupal_get_path('module', 'mollom') . '/images/powered-by-mollom-2.gif?captchaId=' . $captchaId;
- $storage = variable_get('mollom_test_server_captcha', array());
- $storage[$captchaId] = $data;
- variable_set('mollom_test_server_captcha', $storage);
- return $response;
- }
- /**
- * API callback for mollom.checkCaptcha to validate a CAPTCHA response.
- */
- function mollom_test_server_check_captcha($data) {
- $response = array();
- if (isset($data['solution']) && $data['solution'] == 'correct') {
- $response['solved'] = TRUE;
- }
- else {
- $response['solved'] = FALSE;
- $response['reason'] = '';
- }
- $storage = variable_get('mollom_test_server_captcha', array());
- $captchaId = $data['id'];
- if (!isset($storage[$captchaId])) {
- return MENU_NOT_FOUND;
- }
- $storage[$captchaId] = array_merge($storage[$captchaId], $data);
- $response['id'] = $captchaId;
- variable_set('mollom_test_server_captcha', $storage);
- if (isset($storage[$captchaId]['contentId'])) {
- $contentId = $storage[$captchaId]['contentId'];
- $content_captchas = variable_get('mollom_test_server_content_captcha', array());
- $content_captchas[$contentId] = $response['solved'];
- variable_set('mollom_test_server_content_captcha', $content_captchas);
- }
- return $response;
- }
|