diff --git a/mollom.admin.inc b/mollom.admin.inc index 0d96597..3b806e4 100644 --- a/mollom.admin.inc +++ b/mollom.admin.inc @@ -444,12 +444,13 @@ function mollom_admin_blacklist_form($form, &$form_state, $type = 'spam') { $form['blacklist'] = array(); // Do not retrieve the current blacklist when submitting the form. - $blacklist = (empty($form_state['input']) ? mollom('mollom.listBlacklistText') : array()); + $blacklist = (empty($form_state['input']) ? mollom()->getBlacklist() : array()); if (is_array($blacklist)) { - foreach ($blacklist as $id => $entry) { + foreach ($blacklist as $entry) { if ($entry['reason'] != $type) { continue; } + $id = $entry['id']; // #class property is internally used by // theme_mollom_admin_blacklist_form(). $row = array( @@ -469,13 +470,9 @@ function mollom_admin_blacklist_form($form, &$form_state, $type = 'spam') { $row['actions']['delete'] = array( '#type' => 'link', '#title' => t('delete'), - '#href' => 'admin/config/content/mollom/blacklist/delete', + '#href' => 'admin/config/content/mollom/blacklist/delete/' . $entry['id'], '#options' => array( - 'query' => array( - 'text' => $entry['text'], - 'context' => $entry['context'], - 'reason' => $entry['reason'], - ) + drupal_get_destination(), + 'query' => drupal_get_destination(), ), ); $form['blacklist'][$id] = $row; @@ -539,7 +536,7 @@ function mollom_admin_blacklist_form_submit($form, &$form_state) { 'match' => $form_state['values']['entry']['match'], 'reason' => $form_state['values']['entry']['reason'], ); - $result = mollom('mollom.addBlacklistText', $data); + $result = mollom()->createBlacklistEntry($data); $args = array( '@text' => $data['text'], @@ -547,7 +544,7 @@ function mollom_admin_blacklist_form_submit($form, &$form_state) { '@match' => $data['match'], '@reason' => $data['reason'], ); - if ($result === TRUE) { + if (!empty($result['id'])) { drupal_set_message(t('The entry was added to the blacklist.')); _mollom_watchdog(array( 'Added @text (@context, @match) to @reason blacklist.' => $args, @@ -625,23 +622,16 @@ function theme_mollom_admin_blacklist_form($variables) { * @ingroup forms * @see mollom_admin_blacklist_delete_submit() */ -function mollom_admin_blacklist_delete($form, &$form_state) { - $form['text'] = array( - '#type' => 'value', - '#value' => $_GET['text'], - ); - $form['context'] = array( +function mollom_admin_blacklist_delete($form, &$form_state, $entryId) { + $entry = mollom()->getBlacklistEntry($entryId); + $form['entry'] = array( '#type' => 'value', - '#value' => $_GET['context'], - ); - $form['reason'] = array( - '#type' => 'value', - '#value' => $_GET['reason'], + '#value' => $entry, ); return confirm_form( $form, - t('Are you sure you want to delete %text from the blacklist?', array('%text' => $_GET['text'])), + t('Are you sure you want to delete %text from the blacklist?', array('%text' => $entry['text'])), 'admin/config/content/mollom/blacklist', t('This action cannot be undone.'), t('Delete'), t('Cancel') @@ -652,23 +642,18 @@ function mollom_admin_blacklist_delete($form, &$form_state) { * Form submit handler to delete an entry from the blacklist. */ function mollom_admin_blacklist_delete_submit($form, &$form_state) { - $data = array( - 'text' => $form_state['values']['text'], - 'context' => $form_state['values']['context'], - 'reason' => $form_state['values']['reason'], - ); - $result = mollom('mollom.removeBlacklistText', $data); + $result = mollom()->deleteBlacklistEntry($form_state['values']['entry']['id']); $args = array( - '@text' => $data['text'], - '@context' => $data['context'], - '@reason' => $data['reason'], + '@text' => $form_state['values']['entry']['text'], + '@context' => $form_state['values']['entry']['context'], + '@reason' => $form_state['values']['entry']['reason'], ); if ($result === TRUE) { drupal_set_message(t('The entry was removed from the blacklist.')); _mollom_watchdog(array( 'Removed @text (@context) from @reason blacklist.' => $args, - 'Data:
@data' => array('@data' => $data), + 'Data:
@data' => array('@data' => $form_state['values']['entry']), 'Result:
@result' => array('@result' => $result), )); } @@ -676,7 +661,7 @@ function mollom_admin_blacklist_delete_submit($form, &$form_state) { drupal_set_message(t('An error occurred upon trying to remove the item from the blacklist.'), 'error'); _mollom_watchdog(array( 'Failed to removed @text (%context) from @reason blacklist.' => $args, - 'Data:
@data' => array('@data' => $data), + 'Data:
@data' => array('@data' => $form_state['values']['entry']), 'Result:
@result' => array('@result' => $result), ), WATCHDOG_ERROR); } @@ -692,6 +677,9 @@ function mollom_admin_blacklist_delete_submit($form, &$form_state) { * mollom.verifyKey would invalidate the keys and throw an error; hence, * _mollom_fallback() would invoke form_set_error(), effectively preventing this * form from submitting. + * + * @todo Implement proper form validation now that mollom() no longer triggers + * the fallback mode. */ function mollom_admin_settings($form, &$form_state) { // Output a positive status message, since users keep on asking whether diff --git a/mollom.drupal.inc b/mollom.drupal.inc new file mode 100644 index 0000000..9fe0dc1 --- /dev/null +++ b/mollom.drupal.inc @@ -0,0 +1,354 @@ + 'mollom_public_key', + 'privateKey' => 'mollom_private_key', + 'servers' => 'mollom_servers', + ); + + /** + * Implements Mollom::loadConfiguration(). + */ + public function loadConfiguration($name) { + $name = $this->configuration_map[$name]; + return variable_get($name); + } + + /** + * Implements Mollom::saveConfiguration(). + */ + public function saveConfiguration($name, $value) { + $name = $this->configuration_map[$name]; + return variable_set($name, $value); + } + + /** + * Implements Mollom::deleteConfiguration(). + */ + public function deleteConfiguration($name) { + $name = $this->configuration_map[$name]; + return variable_del($name); + } + + /** + * Implements Mollom::getClientInformation(). + */ + public function getClientInformation() { + if ($cache = cache_get('mollom_version')) { + return $cache->data; + } + + // Retrieve Drupal distribution and installation profile information. + $profile = drupal_get_profile(); + $profile_info = system_get_info('module', $profile) + array( + 'distribution_name' => 'Drupal', + 'version' => VERSION, + ); + + // Retrieve Mollom module information. + $mollom_info = system_get_info('module', 'mollom'); + if (empty($mollom_info['version'])) { + // Manually build a module version string for repository checkouts. + $mollom_info['version'] = DRUPAL_CORE_COMPATIBILITY . '-1.x-dev'; + } + + $data = array( + 'platformName' => $profile_info['distribution_name'], + 'platformVersion' => $profile_info['version'], + 'clientName' => $mollom_info['name'], + 'clientVersion' => $mollom_info['version'], + ); + cache_set('mollom_version', $data); + + return $data; + } + + /** + * Overrides Mollom::writeLog(). + */ + function writeLog() { + $messages = array(); + foreach ($this->log as $i => $entry) { + $entry += array('arguments' => array()); + $message = array( + $entry['message'] => $entry['arguments'], + ); + if (isset($entry['data'])) { + $message['Request: @request
@parameters'] = array( + '@request' => $entry['request'], + '@parameters' => $entry['data'], + ); + } + if (isset($entry['response'])) { + $message['Response:
@response'] = array('@response' => $entry['response']); + } + $messages[] = $message; + + // Translate log messages for debugging without watchdog. + // @todo Temporary? + $output = array(); + foreach ($message as $text => $args) { + foreach ($args as &$arg) { + if (is_array($arg)) { + $arg = var_export($arg, TRUE); + } + } + $output[] = strtr($text, $args); + } + $this->log[$i]['message'] = implode("\n", $output); + unset($this->log[$i]['arguments']); +// drupal_set_message(implode('
@data' => array('@data' => $data), @@ -1544,25 +1509,14 @@ function mollom_validate_analysis(&$form, &$form_state) { $form['mollom']['captcha']['#access'] = TRUE; $form['mollom']['captcha']['#required'] = TRUE; - $captcha_data = array( - 'session_id' => $result['session_id'], - ); - $captcha = mollom_get_captcha('image', $captcha_data); - + $captcha = mollom_get_captcha($form_state); // If we get a response, add the image CAPTCHA to the form element. - if (isset($captcha['response']['session_id']) && !empty($captcha['markup'])) { - $form_state['mollom']['response']['session_id'] = $captcha['response']['session_id']; - $form['mollom']['session_id']['#value'] = $captcha['response']['session_id']; - $form['mollom']['captcha']['#field_prefix'] = $captcha['markup']; + if (!empty($captcha)) { + $form['mollom']['captchaId']['#value'] = $form_state['mollom']['response']['captcha']['captchaId']; + $form['mollom']['captcha']['#field_prefix'] = $captcha; } } break; - - case MOLLOM_ANALYSIS_UNKNOWN: - default: - // If we end up here, something went totally wrong. - _mollom_fallback(); - break; } } } @@ -1604,32 +1558,32 @@ function mollom_validate_captcha(&$form, &$form_state) { return; } $data = array( - 'session_id' => $form_state['mollom']['response']['session_id'], - 'captcha_result' => $form_state['values']['mollom']['captcha'], - 'author_ip' => $all_data['author_ip'], + 'captchaId' => $form_state['mollom']['response']['captcha']['captchaId'], + 'solution' => $form_state['values']['mollom']['captcha'], + 'authorIp' => $all_data['authorIp'], ); - if (isset($all_data['author_id'])) { - $data['author_id'] = $all_data['author_id']; + if (isset($all_data['authorId'])) { + $data['authorId'] = $all_data['authorId']; } if (isset($all_data['honeypot'])) { $data['honeypot'] = $all_data['honeypot']; } - $result = mollom('mollom.checkCaptcha', $data); + $result = mollom()->checkCaptcha($data); // Use all available data properties for log messages below. $data += $all_data; // Invoke fallback behavior upon a server error; communication errors are // handled by mollom() already. A server error may happen in case of an // expired or invalid session_id. - if ($result === MOLLOM_ERROR) { + if (!is_array($result) || !isset($result['captchaId'])) { return _mollom_fallback(); } // Store the response for #submit handlers. $form_state['mollom']['response']['captcha'] = $result; - $form['mollom']['session_id']['#value'] = $form_state['mollom']['response']['session_id']; + $form['mollom']['captchaId']['#value'] = $form_state['mollom']['response']['captcha']['captchaId']; - if ($result === TRUE) { + if (!empty($result['solved'])) { $form_state['mollom']['passed_captcha'] = TRUE; $form['mollom']['captcha']['#access'] = FALSE; @@ -1712,11 +1666,18 @@ function mollom_form_submit($form, &$form_state) { // the mapped post_id. $values = mollom_form_get_values($form_state['values'], array(), $form_state['mollom']['mapping']); // We only consider non-empty and non-zero values as valid entity ids. - if (!empty($values['post_id'])) { + if (!empty($values['postId'])) { // Save the Mollom session data. - $data = (object) $form_state['mollom']['response']; + $response = array(); + if (isset($form_state['mollom']['response']['content'])) { + $response += $form_state['mollom']['response']['content']; + } + if (isset($form_state['mollom']['response']['captcha'])) { + $response += $form_state['mollom']['response']['captcha']; + } + $data = (object) $response; $data->entity = $form_state['mollom']['entity']; - $data->id = $values['post_id']; + $data->id = $values['postId']; $data->form_id = $form_state['mollom']['form_id']; // Set the moderation flag for forms accepting bad posts. $data->moderate = $form_state['mollom']['require_moderation']; @@ -1730,141 +1691,23 @@ function mollom_form_submit($form, &$form_state) { */ /** - * Call a remote procedure at the Mollom server. - * - * This function automatically adds the information required to authenticate - * against Mollom. + * Instantiates a new Mollom client. * - * @todo Currently, this function's return value mixes actual values and - * error values. We should rewrite the error handling so that calling - * functions can properly handle error situations. + * @param $class + * (optional) The name of a Mollom client implementation class to instantiate. + * Overrides the 'mollom_class' configuration variable. Debug use only. */ -function mollom($method, $data = array()) { - module_load_include('inc', 'mollom'); - $messages = array(); - - // Initialize refresh variable. - $refresh = FALSE; - - // Enable testing mode. - if (variable_get('mollom_testing_mode', 0)) { - $data['testing'] = TRUE; +function mollom($class = NULL) { + $instance = &drupal_static(__FUNCTION__); + if (!isset($class)) { + $class = variable_get('mollom_class', 'MollomDrupal'); } - - // Retrieve the list of Mollom servers from the database. - $servers = variable_get('mollom_servers', array()); - - if (empty($servers)) { - // Retrieve a new list of servers. - $servers = _mollom_retrieve_server_list(); - // If API keys are invalid, a XML-RPC error code is returned. - if (!is_array($servers)) { - return $servers; - } - - $messages[] = array( - 'Refreshed servers: %servers' => array('%servers' => implode(', ', $servers)), - ); - - // Store the list of servers in the database. - variable_set('mollom_servers', $servers); + // If there is no instance yet or if it is not from the configured class, + // create a new one. + if (!isset($instance) || !($instance instanceof $class)) { + $instance = new $class(); } - - if (is_array($servers)) { - // Send the request to the first server; if that fails, try the other - // servers in the list. - reset($servers); - while ($server = current($servers)) { - $result = xmlrpc($server . '/' . MOLLOM_API_VERSION, array( - $method => array($data + _mollom_authentication()), - )); - - if ($result === FALSE && ($error = xmlrpc_error())) { - if ($error->code === MOLLOM_REFRESH) { - // Avoid endless loops. - if (!$refresh) { - $refresh = TRUE; - - // Retrieve a new list of valid Mollom servers. - $servers = _mollom_retrieve_server_list(); - // If API keys are invalid, the XML-RPC error code is returned. - // To reach this, we must have had a server list (and therefore - // valid keys) before, so we do not immediately return (like above), - // but instead trigger the fallback mode. - if (!is_array($servers)) { - break; - } - - // Reset the list of servers to restart from the first server. - reset($servers); - - // Update the server list. - variable_set('mollom_servers', $servers); - - $messages[] = array( - 'Refreshed servers: %servers' => array('%servers' => implode(', ', $servers)), - ); - } - } - elseif ($error->code === MOLLOM_REDIRECT) { - // Try the next server in the list. - $next = next($servers); - - $messages[] = array( - 'Server %server redirected to: %next.' => array('%server' => $server, '%next' => $next), - ); - } - else { - $messages[] = array( - 'Error @errno from %server for %method: %message' => array( - '@errno' => $error->code, - '%server' => $server, - '%method' => $method, - '%message' => $error->message, - ), - 'Data:
@data' => array('@data' => $data), - ); - - // Instantly return upon a 'real' error. - if ($error->code === MOLLOM_ERROR) { - _mollom_watchdog_multiple($messages, WATCHDOG_ERROR); - return MOLLOM_ERROR; - } - // Otherwise, try the next server. - next($servers); - } - } - else { - _mollom_watchdog_multiple($messages, WATCHDOG_DEBUG); - return $result; - } - } - } - - // If none of the servers worked, activate the fallback mechanism. - // @todo mollom() can be invoked outside of form processing. _mollom_fallback() - // unconditionally invokes form_set_error(), which always displays the - // fallback error message. Ideally, we would pass a $verbose argument to - // _mollom_fallback(), but for that, we'd have to know here already. - // Consequently, mollom() would need that $verbose argument. In the end, we - // likely want to either embed the fallback handling into form processing, - // or introduce a new helper function that is invoked instead of mollom() - // during form processing. - if ($method != 'mollom.verifyKey') { - _mollom_fallback(); - } - - // If everything failed, we reset the server list to force Mollom to request - // a new list. - variable_del('mollom_servers'); - - // Report this error. - $messages[] = array( - 'All servers unreachable or returning errors. The server list was emptied.' => array(), - ); - _mollom_watchdog_multiple($messages, WATCHDOG_ERROR); - - return NETWORK_ERROR; + return $instance; } /** @@ -1901,18 +1744,18 @@ function _mollom_watchdog(array $parts, $severity = WATCHDOG_NOTICE) { // Prettify replacement token values, if possible. foreach ($arguments as $token => $array) { - $flat_value = FALSE; - if (is_array($array)) { - $flat_value = ''; - foreach ($array as $key => $value) { - if (is_array($value)) { - $flat_value = FALSE; - break; - } - $value = var_export($value, TRUE); - // Indent the new value, so there is a visual separation from the last. - $flat_value .= " {$key} = {$value}\n"; + if (!is_array($array)) { + continue; + } + $flat_value = ''; + foreach ($array as $key => $value) { + if (is_array($value)) { + $flat_value = FALSE; + break; } + $value = var_export($value, TRUE); + // Indent the new value, so there is a visual separation from the last. + $flat_value .= " {$key} = {$value}\n"; } // Only convert one-dimensional arrays, or we would lose debugging data. if ($flat_value !== FALSE) { @@ -1942,65 +1785,15 @@ function _mollom_watchdog_multiple($messages, $severity) { } /** - * Returns version information to send with mollom.verifyKey. - * - * Retrieves platform and module version information for mollom.verifyKey, which - * is normally invoked on Mollom's administration pages only. - * - * This information is solely used to speed up support requests and technical - * inquiries. The data may also be aggregated to help the Mollom staff to make - * decisions on new features or the necessity of back-porting improved - * functionality to older versions. - * - * @return - * An array containing: - * - platform_name: The name of the Drupal distribution; i.e., "Drupal". - * - platform_version: The version of Drupal; e.g., "7.0". - * - client_name: The module name; i.e., "Mollom". - * - client_version: The version of the module; e.g., "7.x-1.0". - * - * @see _mollom_status() - */ -function _mollom_get_version() { - if ($cache = cache_get('mollom_version')) { - return $cache->data; - } - - // Retrieve Drupal distribution and installation profile information. - $profile = drupal_get_profile(); - $profile_info = system_get_info('module', $profile) + array( - 'distribution_name' => 'Drupal', - 'version' => VERSION, - ); - - // Retrieve Mollom module information. - $mollom_info = system_get_info('module', 'mollom'); - if (empty($mollom_info['version'])) { - // Manually build a module version string for repository checkouts. - $mollom_info['version'] = DRUPAL_CORE_COMPATIBILITY . '-1.x-dev'; - } - - $data = array( - 'platform_name' => $profile_info['distribution_name'], - 'platform_version' => $profile_info['version'], - 'client_name' => $mollom_info['name'], - 'client_version' => $mollom_info['version'], - ); - cache_set('mollom_version', $data); - - return $data; -} - -/** * Send feedback to Mollom. */ -function _mollom_send_feedback($session_id, $feedback = 'spam') { - $result = mollom('mollom.sendFeedback', array( - 'session_id' => $session_id, - 'feedback' => $feedback, +function _mollom_send_feedback($contentId, $feedback = 'spam') { + $result = mollom()->sendFeedback(array( + 'contentId' => $contentId, + 'moderated' => $feedback, )); _mollom_watchdog(array( - 'Reported %feedback for session id %session.' => array('%session' => $session_id, '%feedback' => $feedback), + 'Reported %feedback for content ID %contentId.' => array('%contentId' => $contentId, '%feedback' => $feedback), )); return $result; } @@ -2033,8 +1826,8 @@ function mollom_get_statistics($refresh = FALSE) { )); foreach ($statistics as $statistic) { - $result = mollom('mollom.getStatistics', array('type' => $statistic)); - if ($result === NETWORK_ERROR || $result === MOLLOM_ERROR) { + $result = mollom()->getStatistics(array('type' => $statistic)); + if ($result === Mollom::NETWORK_ERROR || $result === Mollom::AUTH_ERROR) { // If there was an error, stop fetching statistics and store FALSE // in the cache. This will help prevent from making unnecessary // requests to Mollom if the service is down or the server cannot @@ -2091,66 +1884,69 @@ function mollom_field_extra_fields() { /** * Get the HTML markup for a Mollom CAPTCHA. * - * @param $type - * The CAPTCHA type to retrieve, e.g. 'image' or 'audio'. - * @param $data - * An optional array of parameters to send to Mollom when requesting the - * CAPTCHA. + * @param $form_state + * The current state of a form. * * @return - * An array with the following key/value pairs: - * - 'data': An array of parameters sent to Mollom when requesting the - * CAPTCHA. - * - 'response': An array with the response from Mollom. - * - 'markup': The markup of the CAPTCHA HTML. - */ -function mollom_get_captcha($type, array $data = array()) { - $data += array( - 'author_ip' => ip_address(), - 'ssl' => isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on', - ); + * The markup of the CAPTCHA HTML. + */ +function mollom_get_captcha(&$form_state) { + $key = 'captcha_url_' . $form_state['mollom']['captcha_type']; + if (empty($form_state['mollom']['response'][$key])) { + $data = array( + 'type' => $form_state['mollom']['captcha_type'], + 'authorIp' => ip_address(), + 'ssl' => (int) (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on'), + ); + if (!empty($form_state['mollom']['response']['content']['contentId'])) { + $data['contentId'] = $form_state['mollom']['response']['content']['contentId']; + } + $result = mollom()->getCaptcha($data); + + if (isset($result['url'])) { + $url = $result['url']; + $form_state['mollom']['response'][$key] = $url; + $form_state['mollom']['response']['captcha']['captchaId'] = $result['captchaId']; + } + else { + return ''; + } + } + else { + $url = $form_state['mollom']['response'][$key]; + } // @todo Convert these to actual theme functions? $output = ''; - switch ($type) { + switch ($form_state['mollom']['captcha_type']) { case 'audio': - $response = mollom('mollom.getAudioCaptcha', $data); - if ($response) { - $source = url(base_path() . drupal_get_path('module', 'mollom') . '/mollom-captcha-player.swf', array( - 'query' => array('url' => $response['url']), - 'external' => TRUE, - )); - $output = ''; - - $output = '' . $output . ''; - $output .= ' (' . t('verify using image') . ')'; - } + $source = url(base_path() . drupal_get_path('module', 'mollom') . '/mollom-captcha-player.swf', array( + 'query' => array('url' => $url), + 'external' => TRUE, + )); + $output = ''; + + $output = '' . $output . ''; + $output .= ' (' . t('verify using image') . ')'; break; case 'image': - $response = mollom('mollom.getImageCaptcha', $data); - if ($response) { - $captcha = theme('image', array('path' => url($response['url']), 'alt' => t('Type the characters you see in this picture.'), 'getsize' => FALSE)); - $output = '' . $captcha . ''; - $output .= ' (' . t('verify using audio') . ')'; - } + $captcha = theme('image', array('path' => $url, 'alt' => t('Type the characters you see in this picture.'), 'getsize' => FALSE)); + $output = '' . $captcha . ''; + $output .= ' (' . t('verify using audio') . ')'; break; } - return array( - 'data' => $data, - 'response' => $response, - 'markup' => $output, - ); + return $output; } /** diff --git a/mollom.pages.inc b/mollom.pages.inc index a7f02a5..82d9439 100644 --- a/mollom.pages.inc +++ b/mollom.pages.inc @@ -12,18 +12,22 @@ * The new CAPTCHA type to retrieve, e.g. 'image' or 'audio'. * @param $form_build_id * The internal form build id of the form to update the CAPTCHA for. - * @param $mollom_session_id - * The last known Mollom session id contained in the form. + * @param $captchaId + * The last known Mollom CAPTCHA ID contained in the form. * * @return * A JSON array containing: * - content: The HTML markup for the new CAPTCHA. - * - session_id: The Mollom session id for the new CAPTCHA. + * - captchaId: The ID for the new CAPTCHA. * * @todo Add error handling. */ -function mollom_captcha_js($type, $form_build_id, $mollom_session_id) { - $captcha = mollom_get_captcha($type, array('session_id' => $mollom_session_id)); +function mollom_captcha_js($type, $form_build_id, $captchaId) { + $dummy_state['mollom'] = array( + 'captcha_type' => $type, + ); + $dummy_state['mollom']['response']['captcha']['captchaId'] = $captchaId; + $captcha = mollom_get_captcha($dummy_state); // Update cached session id in the cached $form_state. // We rely on native form caching of Form API to store our Mollom session @@ -36,20 +40,21 @@ function mollom_captcha_js($type, $form_build_id, $mollom_session_id) { // id. Therefore, we need to update the session id in the cached $form_state. // @todo Replace the entire CAPTCHA switch/refresh with new AJAX framework // functionality. - if (!empty($captcha['response']['session_id'])) { + if (!empty($dummy_state['mollom']['response']['session_id'])) { if ($cache = cache_get('form_state_' . $form_build_id, 'cache_form')) { $form_state = $cache->data; - $form_state['mollom']['response']['session_id'] = $captcha['response']['session_id']; + $form_state['mollom']['response']['captcha']['captchaId'] = $dummy_state['mollom']['response']['captcha']['captchaId']; + // @todo Double-check $cid. cache_set('form_state_' . $form_build_id, $form_state, 'cache_form', REQUEST_TIME + 21600); - // After successfully updating the cache, replace the original session id. - $mollom_session_id = $captcha['response']['session_id']; + // After successfully updating the cache, replace the original ID. + $captchaId = $form_state['mollom']['response']['captcha']['captchaId']; } } // Return new content and new session_id via JSON. $data = array( - 'content' => $captcha['markup'], - 'session_id' => $mollom_session_id, + 'content' => $captcha, + 'captchaId' => $captchaId, ); drupal_json_output($data); drupal_exit(); @@ -101,16 +106,15 @@ function mollom_report_form_submit($form, &$form_state) { // Load the Mollom session data. if ($entity == 'session') { $data = new stdClass; - $data->session_id = $id; + $data->contentId = $id; } else { $data = mollom_data_load($entity, $id); } // Send feedback to Mollom, if we have session data. - if (isset($data->session_id) && !empty($form_state['values']['mollom']['feedback'])) { - // @todo Check the actual reponse. - _mollom_send_feedback($data->session_id, $form_state['values']['mollom']['feedback']); + if (isset($data->contentId) && !empty($form_state['values']['mollom']['feedback'])) { + _mollom_send_feedback($data->contentId, $form_state['values']['mollom']['feedback']); drupal_set_message(t('The content was successfully reported as inappropriate.')); } diff --git a/tests/mollom.class.test b/tests/mollom.class.test new file mode 100644 index 0000000..1d9ef81 --- /dev/null +++ b/tests/mollom.class.test @@ -0,0 +1,218 @@ + 'Mollom class', + 'description' => 'Tests Mollom class functionality.', + 'group' => 'Mollom', + ); + } + + function setUp() { + parent::setUp(); + // DrupalUnitTestCase does not autoload classes for whatever reason. + module_load_include('inc', 'mollom'); + } + + /** + * Asserts that two values belonging to the same variable are equal. + * + * Checks to see whether two values, which belong to the same variable name or + * identifier, are equal and logs a readable assertion message. + * + * @param $name + * A name or identifier to use in the assertion message. + * @param $first + * The first value to check. + * @param $second + * The second value to check. + * + * @return + * TRUE if the assertion succeeded, FALSE otherwise. + * + * @see MollomWebTestCase::assertNotSame() + * + * @todo D8: Move into core. This improved assertEqual() did not get into D7, + * since the function signature differs and it's plenty of work to manually + * update all assertEqual() invocations throughout all tests. + */ + protected function assertSame($name, $first, $second) { + $message = t("@name: @first is equal to @second.", array( + '@name' => $name, + '@first' => var_export($first, TRUE), + '@second' => var_export($second, TRUE), + )); + $this->assertEqual($first, $second, $message); + } + + /** + * Asserts that two values belonging to the same variable are not equal. + * + * Checks to see whether two values, which belong to the same variable name or + * identifier, are not equal and logs a readable assertion message. + * + * @param $name + * A name or identifier to use in the assertion message. + * @param $first + * The first value to check. + * @param $second + * The second value to check. + * + * @return + * TRUE if the assertion succeeded, FALSE otherwise. + * + * @see MollomWebTestCase::assertSame() + */ + protected function assertNotSame($name, $first, $second) { + $message = t("@name: @first is not equal to @second.", array( + '@name' => $name, + '@first' => var_export($first, TRUE), + '@second' => var_export($second, TRUE), + )); + $this->assertNotEqual($first, $second, $message); + } + + /** + * Tests Mollom::httpBuildQuery(). + */ + function testHttpBuildQuery() { + $input = array('foo' => 1, 'bar' => 2); + $expected = 'foo=1&bar=2'; + $this->assertSame(var_export($input, TRUE), Mollom::httpBuildQuery($input), $expected); + + $input = array('checks' => array('foo' => 'spam', 'bar' => 'profanity')); + $expected = 'checks[foo]=spam&checks[bar]=profanity'; + $this->assertSame(var_export($input, TRUE), Mollom::httpBuildQuery($input), $expected); + + $input = array('checks' => array('spam', 'profanity')); + $expected = 'checks=spam&checks=profanity'; + $this->assertSame(var_export($input, TRUE), Mollom::httpBuildQuery($input), $expected); + + $input = array('checks' => array(array('spam'), array('profanity'))); + $expected = 'checks=spam&checks=profanity'; + $this->assertSame(var_export($input, TRUE), Mollom::httpBuildQuery($input), $expected); + + $input = array('checks' => array('spam', '')); + $expected = 'checks=spam&checks='; + $this->assertSame(var_export($input, TRUE), Mollom::httpBuildQuery($input), $expected); + + $input = array('checks' => 'spam'); + $expected = 'checks=spam'; + $this->assertSame(var_export($input, TRUE), Mollom::httpBuildQuery($input), $expected); + } + + /** + * Tests Mollom::httpParseQuery(). + */ + function testHttpParseQuery() { + $input = 'foo=1&bar=2'; + $expected = array('foo' => 1, 'bar' => 2); + $this->assertSame($input, Mollom::httpParseQuery($input), $expected); + + $input = 'checks=spam&checks=profanity'; + $expected = array('checks' => array('spam', 'profanity')); + $this->assertSame($input, Mollom::httpParseQuery($input), $expected); + + // Mollom::httpParseQuery() does not attempt to work transparently. Thus, + // multiple parameter names containing brackets itself (regular PHP syntax) + // will lead to an "unexpected" result. Although it wouldn't be hard to add + // support for this, there's currently no need for it. + $input = 'checks[]=spam&checks[]=profanity'; + $expected = array('checks' => array(array('spam'), array('profanity'))); + $this->assertSame($input, Mollom::httpParseQuery($input), $expected); + + $input = 'checks=spam&checks='; + $expected = array('checks' => array('spam', '')); + $this->assertSame($input, Mollom::httpParseQuery($input), $expected); + + $input = 'checks=spam&checks'; + $expected = array('checks' => array('spam', '')); + $this->assertSame($input, Mollom::httpParseQuery($input), $expected); + + $input = 'checks=spam&'; + $expected = array('checks' => 'spam'); + $this->assertSame($input, Mollom::httpParseQuery($input), $expected); + + $input = 'checks=spam'; + $expected = array('checks' => 'spam'); + $this->assertSame($input, Mollom::httpParseQuery($input), $expected); + } + + /** + * Tests Mollom::parseXML(). + */ + function testParseXML() { + $header = ''; + + $input = $header . <<
0
+ en
+ de
+ 0
+ @data', array('%entity' => $entity, '@id' => $id, '@data' => var_export($data, TRUE)))); - if (isset($session_id)) { - $this->assertSame(t('Stored session id'), $data->session_id, $session_id); + $this->assertTrue($data->id, t('Mollom session data for %entity @id exists:
@data', array( + '%entity' => $entity, + '@id' => $id, + '@data' => var_export($data, TRUE), + ))); + if (isset($response_id)) { + $this->assertSame(t('Stored session id'), $data->$response_type, $response_id); } return $data; } @@ -584,12 +629,16 @@ class MollomWebTestCase extends DrupalWebTestCase { * * @see MollomWebTestCase::resetServerRecords() * @see mollom_test_xmlrpc() + * + * @todo Needs update. May directly use the short name suffixes of testing + * server API functions; i.e., 'content', 'captcha', 'blacklist', etc. */ protected function getServerRecord($method = 'mollom.checkContent') { // Map the XML-RPC method name to the corresponding function callback name. drupal_load('module', 'mollom_test'); $method_function_map = mollom_test_xmlrpc(); $function = $method_function_map[$method]; + $function = strtr($function, array('xmlrpc_' => '')); // Retrieve last recorded values. $storage = variable_get($function, array()); @@ -614,6 +663,7 @@ class MollomWebTestCase extends DrupalWebTestCase { drupal_load('module', 'mollom_test'); $method_function_map = mollom_test_xmlrpc(); $function = $method_function_map[$method]; + $function = strtr($function, array('xmlrpc_' => '')); // Delete the variable. variable_del($function); @@ -630,7 +680,7 @@ class MollomWebTestCase extends DrupalWebTestCase { * * @see DrupalWebTestCase->drupalGet() * @see MollomWebTestCase->assertMollomWatchdogMessages() - * @see MollomWebTestCase->assertSessionID() + * @see MollomWebTestCase->assertResponseID() */ protected function drupalGet($path, array $options = array(), array $headers = array()) { $output = parent::drupalGet($path, $options, $headers); @@ -649,7 +699,7 @@ class MollomWebTestCase extends DrupalWebTestCase { * negate the watchdog message severity assertion. * * @see MollomWebTestCase->assertMollomWatchdogMessages() - * @see MollomWebTestCase->assertSessionID() + * @see MollomWebTestCase->assertResponseID() * @see DrupalWebTestCase->drupalPost() */ protected function drupalPost($path, $edit, $submit, array $options = array(), array $headers = array(), $form_html_id = NULL, $extra_post = NULL) { @@ -731,10 +781,6 @@ class MollomInstallationTestCase extends MollomWebTestCase { } function setUp() { - // Re-initialize stored session_id and watchdog messages. - $this->resetSessionID(); - $this->messages = array(); - $this->disableDefaultSetup = TRUE; parent::setUp(array('comment')); @@ -884,150 +930,154 @@ class MollomResponseTestCase extends MollomWebTestCase { * Tests mollom.checkContent(). */ function testCheckContent() { + $mollom = mollom(); $data = array( - 'author_name' => $this->admin_user->name, - 'author_mail' => $this->admin_user->mail, - 'author_id' => $this->admin_user->uid, - 'author_ip' => ip_address(), + 'authorName' => $this->admin_user->name, + 'authorMail' => $this->admin_user->mail, + 'authorId' => $this->admin_user->uid, + 'authorIp' => ip_address(), ); // Ensure proper response for 'ham' submissions. // By default (i.e., omitting 'checks') we expect spam and quality checking // only. - $data['post_body'] = 'ham'; - $result = mollom('mollom.checkContent', $data); + $data['postBody'] = 'ham'; + $result = $mollom->checkContent($data); $this->assertMollomWatchdogMessages(); - $this->assertSame('spam', $result['spam'], MOLLOM_ANALYSIS_HAM); - $this->assertSame('quality', $result['quality'], 1); + $this->assertSame('spam', $result['spam'], 0.0); + $this->assertSame('spamResult', $result['spamResult'], 'HAM'); + $this->assertSame('quality', $result['quality'], 1.0); $this->assertTrue(!isset($result['profanity']), 'profanity not returned.'); - $session_id = $this->assertSessionID($result['session_id']); + $data['contentId'] = $this->assertResponseID('contentId', $result['contentId']); // Ensure proper response for 'spam' submissions, re-using session_id. - $data['post_body'] = 'spam'; - $data['session_id'] = $session_id; - $result = mollom('mollom.checkContent', $data); + $data['postBody'] = 'spam'; + $result = $mollom->checkContent($data); $this->assertMollomWatchdogMessages(); - $this->assertSame('spam', $result['spam'], MOLLOM_ANALYSIS_SPAM); - $this->assertSame('quality', $result['quality'], 0); + $this->assertSame('spam', $result['spam'], 1.0); + $this->assertSame('spamResult', $result['spamResult'], 'SPAM'); + $this->assertSame('quality', $result['quality'], 0.0); $this->assertTrue(!isset($result['profanity']), 'profanity not returned.'); - $session_id = $this->assertSessionID($result['session_id']); + $data['contentId'] = $this->assertResponseID('contentId', $result['contentId']); // Ensure proper response for 'unsure' submissions, re-using session_id. - $data['post_body'] = 'unsure'; - $data['session_id'] = $session_id; - $result = mollom('mollom.checkContent', $data); + $data['postBody'] = 'unsure'; + $result = $mollom->checkContent($data); $this->assertMollomWatchdogMessages(); - $this->assertSame('spam', $result['spam'], MOLLOM_ANALYSIS_UNSURE); + $this->assertSame('spam', $result['spam'], 0.5); + $this->assertSame('spamResult', $result['spamResult'], 'UNSURE'); $this->assertSame('quality', $result['quality'], 0.5); $this->assertTrue(!isset($result['profanity']), 'profanity not returned.'); - $session_id = $this->assertSessionID($result['session_id']); + $data['contentId'] = $this->assertResponseID('contentId', $result['contentId']); // Additionally enable profanity checking. - $data['post_body'] = 'spam profanity'; - $data['checks'] = 'spam,quality,profanity'; - $data['session_id'] = $session_id; - $result = mollom('mollom.checkContent', $data); + $data['postBody'] = 'spam profanity'; + $data['checks'] = array('spam', 'quality', 'profanity'); + $result = $mollom->checkContent($data); $this->assertMollomWatchdogMessages(); - $this->assertSame('spam', $result['spam'], MOLLOM_ANALYSIS_SPAM); - $this->assertSame('quality', $result['quality'], 0); - $this->assertSame('profanity', $result['profanity'], 1); - $session_id = $this->assertSessionID($result['session_id']); + $this->assertSame('spam', $result['spam'], 1.0); + $this->assertSame('spamResult', $result['spamResult'], 'SPAM'); + $this->assertSame('quality', $result['quality'], 0.0); + $this->assertSame('profanity', $result['profanity'], 1.0); + $data['contentId'] = $this->assertResponseID('contentId', $result['contentId']); // Change the string to contain profanity only. - $data['post_body'] = 'profanity'; - $data['checks'] = 'spam,quality,profanity'; - $data['session_id'] = $session_id; - $result = mollom('mollom.checkContent', $data); + $data['postBody'] = 'profanity'; + $data['checks'] = array('spam', 'quality', 'profanity'); + $result = $mollom->checkContent($data); $this->assertMollomWatchdogMessages(); - $this->assertSame('spam', $result['spam'], MOLLOM_ANALYSIS_UNSURE); - $this->assertSame('quality', $result['quality'], 0); - $this->assertSame('profanity', $result['profanity'], 1); - $session_id = $this->assertSessionID($result['session_id']); + $this->assertSame('spam', $result['spam'], 0.5); + $this->assertSame('spamResult', $result['spamResult'], 'UNSURE'); + $this->assertSame('quality', $result['quality'], 0.0); + $this->assertSame('profanity', $result['profanity'], 1.0); + $data['contentId'] = $this->assertResponseID('contentId', $result['contentId']); // Disable spam checking, only do profanity checking. - $data['post_body'] = 'spam profanity'; - $data['checks'] = 'profanity'; - $data['session_id'] = $session_id; - $result = mollom('mollom.checkContent', $data); + $data['postBody'] = 'spam profanity'; + $data['checks'] = array('profanity'); + $result = $mollom->checkContent($data); $this->assertMollomWatchdogMessages(); $this->assertTrue(!isset($result['spam']), 'spam not returned.'); $this->assertTrue(!isset($result['quality']), 'quality not returned.'); - $this->assertSame('profanity', $result['profanity'], 1); - $session_id = $this->assertSessionID($result['session_id']); + $this->assertSame('profanity', $result['profanity'], 1.0); + $data['contentId'] = $this->assertResponseID('contentId', $result['contentId']); // Pass arbitrary string to profanity checking. - $data['post_body'] = $this->randomString(12); - $data['session_id'] = $session_id; - $result = mollom('mollom.checkContent', $data); + $data['postBody'] = $this->randomString(12); + $result = $mollom->checkContent($data); $this->assertMollomWatchdogMessages(); $this->assertTrue(!isset($result['spam']), 'spam not returned.'); $this->assertTrue(!isset($result['quality']), 'quality not returned.'); - $this->assertSame('profanity', $result['profanity'], 0); - $session_id = $this->assertSessionID($result['session_id']); + $this->assertSame('profanity', $result['profanity'], 0.0); + $data['contentId'] = $this->assertResponseID('contentId', $result['contentId']); } /** * Tests results of mollom.checkContent() across requests for a single session. */ function testCheckContentSession() { + $mollom = mollom(); $data = array( - 'author_name' => $this->admin_user->name, - 'author_mail' => $this->admin_user->mail, - 'author_id' => $this->admin_user->uid, - 'author_ip' => ip_address(), + 'authorName' => $this->admin_user->name, + 'authorMail' => $this->admin_user->mail, + 'authorId' => $this->admin_user->uid, + 'authorIp' => ip_address(), ); // Sequence: Post unsure spam, correct CAPTCHA, change post into spam, // expect it to be ham (due to correct CAPTCHA). - $data['post_body'] = 'unsure'; - $result = mollom('mollom.checkContent', $data); + $data['postBody'] = 'unsure'; + $result = $mollom->checkContent($data); $this->assertMollomWatchdogMessages(); - $this->assertSame('spam', $result['spam'], MOLLOM_ANALYSIS_UNSURE); - $data['session_id'] = $this->assertSessionID($result['session_id']); + $this->assertSame('spam', $result['spam'], 0.5); + $this->assertSame('spamResult', $result['spamResult'], 'UNSURE'); + $data['contentId'] = $this->assertResponseID('contentId', $result['contentId']); $captcha_data = array( - 'session_id' => $data['session_id'], - 'author_ip' => $data['author_ip'], + 'type' => 'image', + 'contentId' => $data['contentId'], + 'authorIp' => $data['authorIp'], ); - $result = mollom('mollom.getImageCaptcha', $captcha_data); + $result = $mollom->getCaptcha($captcha_data); $this->assertMollomWatchdogMessages(); - $data['session_id'] = $this->assertSessionID($result['session_id']); + $data['captchaId'] = $this->assertResponseID('captchaId', $result['captchaId']); $captcha_data = array( - 'session_id' => $data['session_id'], - 'author_ip' => $data['author_ip'], - 'author_id' => $data['author_id'], - 'captcha_result' => 'correct', + 'captchaId' => $data['captchaId'], + 'contentId' => $data['contentId'], + 'authorIp' => $data['authorIp'], + 'authorId' => $data['authorId'], + 'solution' => 'correct', ); - $result = mollom('mollom.checkCaptcha', $captcha_data); + $result = $mollom->checkCaptcha($captcha_data); $this->assertMollomWatchdogMessages(); - $this->assertIdentical($result, TRUE, t('CAPTCHA response was correct.')); + $this->assertSame('solved', $result['solved'], 1); - $data['post_body'] = 'spam'; - $result = mollom('mollom.checkContent', $data); + $data['postBody'] = 'spam'; + $result = $mollom->checkContent($data); $this->assertMollomWatchdogMessages(); - $this->assertSame('spam', $result['spam'], MOLLOM_ANALYSIS_HAM); - $data['session_id'] = $this->assertSessionID($result['session_id']); + $this->assertSame('spam', $result['spam'], 0.0); + $this->assertSame('spamResult', $result['spamResult'], 'HAM'); + $data['contentId'] = $this->assertResponseID('contentId', $result['contentId']); } /** * Tests mollom.getImageCaptcha(). */ function testGetImageCaptcha() { + $mollom = mollom(); // Ensure we get no SSL URL by default. $data = array( - 'author_ip' => ip_address(), + 'type' => 'image', + 'authorIp' => ip_address(), ); - $result = mollom('mollom.getImageCaptcha', $data); + $result = $mollom->getCaptcha($data); $this->assertMollomWatchdogMessages(); $this->assertTrue(strpos($result['url'], 'http://') === 0, t('CAPTCHA URL uses HTTP protocol.')); // Ensure we get a SSL URL when passing the 'ssl' parameter. - $data = array( - 'author_ip' => ip_address(), - 'ssl' => TRUE, - ); - $result = mollom('mollom.getImageCaptcha', $data); + $data['ssl'] = TRUE; + $result = $mollom->getCaptcha($data); $this->assertMollomWatchdogMessages(); $this->assertTrue(strpos($result['url'], 'https://') === 0, t('CAPTCHA URL uses HTTPS protocol.')); } @@ -1036,22 +1086,24 @@ class MollomResponseTestCase extends MollomWebTestCase { * Tests mollom.checkCaptcha(). */ function testCheckCaptcha() { + $mollom = mollom(); // Ensure we can send an 'author_id'. // Verifying no severe watchdog messages is sufficient, as unsupported // parameters would trigger a XML-RPC error. $uid = rand(); $data = array( - 'author_ip' => ip_address(), - 'author_id' => $uid, + 'type' => 'image', + 'authorIp' => ip_address(), + 'authorId' => $uid, ); - $result = mollom('mollom.getImageCaptcha', $data); + $result = $mollom->getCaptcha($data); $this->assertMollomWatchdogMessages(); + $data['captchaId'] = $this->assertResponseID('captchaId', $result['captchaId']); $data += array( - 'session_id' => $result['session_id'], - 'captcha_result' => 'correct', + 'solution' => 'correct', ); - $result = mollom('mollom.checkCaptcha', $data); + $result = $mollom->checkCaptcha($data); $this->assertMollomWatchdogMessages(); } } @@ -1060,6 +1112,9 @@ class MollomResponseTestCase extends MollomWebTestCase { * Tests low-level communication with local fake Mollom server. */ class MollomResponseLocalTestCase extends MollomResponseTestCase { + // Re-route Mollom communication to this testing site. + protected $mollomClass = 'MollomDrupalTestLocal'; + public static function getInfo() { return array( 'name' => 'Server responses (local)', @@ -1067,13 +1122,6 @@ class MollomResponseLocalTestCase extends MollomResponseTestCase { 'group' => 'Mollom', ); } - - function setUp() { - // Enable testing server implementation. - parent::setUp(array('mollom_test')); - // Re-route Mollom communication to this testing site. - variable_set('mollom_servers', array($GLOBALS['base_url'] . '/xmlrpc.php?version=')); - } } class MollomAccessTestCase extends MollomWebTestCase { @@ -1140,6 +1188,7 @@ class MollomAccessTestCase extends MollomWebTestCase { $this->web_user = $this->drupalCreateUser(array('edit own comments')); $this->drupalLogin($this->web_user); $edit = array( + 'subject' => 'ham', 'comment_body[und][0][value]' => 'ham', ); $this->drupalPost('comment/reply/' . $node->nid, $edit, t('Preview')); @@ -1155,6 +1204,7 @@ class MollomAccessTestCase extends MollomWebTestCase { $this->clickLink('edit'); $edit = array( + 'subject' => 'spam', 'comment_body[und][0][value]' => 'spam', ); $this->drupalPost(NULL, $edit, t('Preview')); @@ -1179,6 +1229,9 @@ class MollomAccessTestCase extends MollomWebTestCase { } class MollomFallbackTestCase extends MollomWebTestCase { + // Re-route Mollom communication to this testing site. + protected $mollomClass = 'MollomDrupalTestLocal'; + public static function getInfo() { return array( 'name' => 'Fallback behavior', @@ -1187,11 +1240,6 @@ class MollomFallbackTestCase extends MollomWebTestCase { ); } - function setUp() { - // Enable testing server implementation. - parent::setUp(array('mollom_test')); - } - /** * Make sure that "request new password" submissions can be blocked when * the Mollom servers are unreachable. @@ -1259,8 +1307,7 @@ class MollomFallbackTestCase extends MollomWebTestCase { variable_set('mollom_servers', array( 'http://fake-host-1', 'http://fake-host-2', - $GLOBALS['base_url'] . '/xmlrpc.php?version=', - 'http://xmlrpc1.mollom.com', // The real server. + $GLOBALS['base_url'] . '/mollom-test/rest', // A real server. 'http://fake-host-3', )); @@ -1276,6 +1323,8 @@ class MollomFallbackTestCase extends MollomWebTestCase { } class MollomServerListRecoveryTestCase extends MollomWebTestCase { + protected $profile = 'testing'; + public static function getInfo() { return array( 'name' => 'Server list recovery', @@ -1301,16 +1350,20 @@ class MollomServerListRecoveryTestCase extends MollomWebTestCase { ); foreach ($list as $servers) { - // Call mollom.verifyKey with an invalid server list. The expected behavior - // is that the first call fails, but that the second call succeeds because - // the server list is automatically reset or recovered by the Mollom module. variable_set('mollom_servers', $servers); - - $key_is_valid = mollom('mollom.verifyKey'); - $this->assertIdentical($key_is_valid, NETWORK_ERROR, t('The Mollom servers could not be contacted.')); + // Reset static, since the Mollom instance is statically cached. + drupal_static_reset('mollom'); + + // Verify that Mollom::query() fails with an invalid server list and + // appropriate messages are logged. + $mollom = mollom(); + $key_is_valid = $mollom->verifyKey(); + $this->assertIdentical($key_is_valid, Mollom::NETWORK_ERROR, t('The Mollom servers could not be contacted.')); $this->assertMollomWatchdogMessages(WATCHDOG_EMERGENCY); - $key_is_valid = mollom('mollom.verifyKey'); + // Verify that Mollom::query() automatically empties the server list and + // a directly following, subsequent request attempt succeeds. + $key_is_valid = $mollom->verifyKey(); $this->assertIdentical($key_is_valid, TRUE, t('The Mollom servers could be contacted.')); $this->assertMollomWatchdogMessages(); } @@ -1318,6 +1371,8 @@ class MollomServerListRecoveryTestCase extends MollomWebTestCase { } class MollomLanguageDetectionTestCase extends MollomWebTestCase { + protected $profile = 'testing'; + public static function getInfo() { return array( 'name' => 'Language detection', @@ -1345,10 +1400,14 @@ class MollomLanguageDetectionTestCase extends MollomWebTestCase { 'zh' => "螽斯羽,诜诜兮。宜尔子孙,振振兮", ); + $mollom = mollom(); foreach ($strings as $language => $text) { - $result = mollom('mollom.detectLanguage', array('text' => $text)); - $this->assertEqual($result[0]['language'], $language, t('A language code was specified and they match.')); - $this->assertTrue($result[0]['confidence'] > 0, t('A confidence value was specified and it is greater than 0.')); + $result = $mollom->checkContent(array( + 'checks' => 'language', + 'postBody' => $text, + )); + $this->assertEqual($result['language'][0]['language'], $language, t('A language code was specified and they match.')); + $this->assertTrue($result['language'][0]['confidence'] > 0, t('A confidence value was specified and it is greater than 0.')); } } } @@ -1378,139 +1437,110 @@ class MollomBlacklistTestCase extends MollomWebTestCase { } /** - * Test the URL blacklist functionality at the API level without using a web interface. + * Test the blacklist functionality at the API level without using a web interface. */ - function testUrlBlacklistAPI() { + function testBlacklistAPI() { + $mollom = mollom(); // Remove any stale blacklist entries from test runs that did not finish. - $blacklist = mollom('mollom.listBlacklistURL'); + $blacklist = $mollom->getBlacklist(); foreach ($blacklist as $entry) { if (REQUEST_TIME - strtotime($entry['created']) > 86400) { - mollom('mollom.removeBlacklistURL', array('url' => $entry['url'])); + $mollom->deleteBlacklistEntry($entry['id']); } } // Blacklist a URL. $domain = $this->randomName() . '.com'; - $result = mollom('mollom.addBlacklistURL', array('url' => 'http://' . $domain)); - $this->assertTrue($result, t('The URL was blacklisted.')); - - // Check whether posts containing the blacklisted URL are properly blocked. - $result = mollom('mollom.checkContent', array( - 'post_body' => "When the exact URL is present, the post should get blocked: http://{$domain}", + $entry = $mollom->createBlacklistEntry(array( + 'value' => $domain, + 'context' => 'allFields', + 'reason' => 'spam', + 'match' => 'contains', )); - $this->assertEqual($result['spam'], MOLLOM_ANALYSIS_SPAM, t('Exact URL match was blocked.')); + $this->assertTrue($entry['id'], t('The URL was blacklisted.')); - $result = mollom('mollom.checkContent', array( - 'post_body' => "When the URL is expanded in the back, the post should get blocked: http://{$domain}/oh-my", + // Check whether posts containing the blacklisted URL are properly blocked. + $result = mollom()->checkContent(array( + 'postBody' => "When the exact URL is present, the post should get blocked: http://{$domain}", )); - $this->assertEqual($result['spam'], MOLLOM_ANALYSIS_SPAM, t('Partial URL match was blocked.')); + $this->assertEqual($result['spamResult'], 'SPAM', t('Exact URL match was blocked.')); - $result = mollom('mollom.checkContent', array( - 'post_body' => "When the URL is expanded in the front, the post should get blocked: http://www.{$domain}", + $result = mollom()->checkContent(array( + 'postBody' => "When the URL is expanded in the back, the post should get blocked: http://{$domain}/oh-my", )); - $this->assertEqual($result['spam'], MOLLOM_ANALYSIS_SPAM, t('URL with www-prefix was blocked.')); + $this->assertEqual($result['spamResult'], 'SPAM', t('Partial URL match was blocked.')); - $result = mollom('mollom.checkContent', array( - 'post_body' => "When the URL has a different schema, the post should get blocked: ftp://www.{$domain}", + $result = mollom()->checkContent(array( + 'postBody' => "When the URL is expanded in the front, the post should get blocked: http://www.{$domain}", )); - $this->assertEqual($result['spam'], MOLLOM_ANALYSIS_SPAM, t('URL with different schema was blocked.')); + $this->assertEqual($result['spamResult'], 'SPAM', t('URL with www-prefix was blocked.')); - // @todo Not implemented yet. - /* - $result = mollom('mollom.checkContent', array( - 'post_body' => "When the domain appears on its own, the post should get blocked: www.{$domain}", + $result = mollom()->checkContent(array( + 'postBody' => "When the URL has a different schema, the post should get blocked: ftp://www.{$domain}", )); - $this->assertEqual($result['spam'], MOLLOM_ANALYSIS_SPAM, t('Plain domain name with www-prefix was blocked.')); - */ + $this->assertEqual($result['spamResult'], 'SPAM', t('URL with different schema was blocked.')); - $result = mollom('mollom.removeBlacklistURL', array('url' => 'http://' . $domain)); - $this->assertTrue($result, t('The blacklisted URL was removed.')); - } - - /** - * Test the text blacklist functionality at the API level without using a web interface. - */ - function testTextBlacklistAPI() { - // Remove any stale blacklist entries from test runs that did not finish. - $blacklist = mollom('mollom.listBlacklistText'); - foreach ($blacklist as $entry) { - if (REQUEST_TIME - strtotime($entry['created']) > 86400) { - mollom('mollom.removeBlacklistText', array( - 'text' => $entry['text'], - 'context' => $entry['context'], - 'reason' => $entry['reason'], - )); - } - } + $result = $mollom->deleteBlacklistEntry($entry['id']); + $this->assertIdentical($result, TRUE, t('The blacklisted URL was removed.')); // Blacklist a word. // @todo As of now, only non-numeric, lower-case text seems to be supported. $term = drupal_strtolower(preg_replace('/[^a-zA-Z]/', '', $this->randomName())); - $data = array( + $entry = $mollom->createBlacklistEntry(array( 'text' => $term, 'context' => 'everything', 'reason' => 'spam', 'match' => 'contains', - ); - $result = mollom('mollom.addBlacklistText', $data); - $this->assertIdentical($result, TRUE, t('The text was blacklisted.')); + )); + $this->assertTrue($entry['id'], t('The text was blacklisted.')); // Check whether posts containing the blacklisted word are properly blocked. $data = array( - 'post_body' => $term, + 'postBody' => $term, ); - $result = mollom('mollom.checkContent', $data); - $this->assertEqual($result['spam'], MOLLOM_ANALYSIS_SPAM, t('Identical match was blocked.')); + $result = mollom()->checkContent($data); + $this->assertEqual($result['spamResult'], 'SPAM', t('Identical match was blocked.')); $data = array( - 'post_body' => "When the term is present, the post should get blocked: " . $term, + 'postBody' => "When the term is present, the post should get blocked: " . $term, ); - $result = mollom('mollom.checkContent', $data); - $this->assertEqual($result['spam'], MOLLOM_ANALYSIS_SPAM, t('Exact match was blocked.')); + $result = mollom()->checkContent($data); + $this->assertEqual($result['spamResult'], 'SPAM', t('Exact match was blocked.')); $data = array( - 'post_body' => "When match is 'contains', the word can be surrounded by other text: abc" . $term . "def", + 'postBody' => "When match is 'contains', the word can be surrounded by other text: abc" . $term . "def", ); - $result = mollom('mollom.checkContent', $data); - $this->assertEqual($result['spam'], MOLLOM_ANALYSIS_SPAM, t('Partial match was blocked.')); + $result = mollom()->checkContent($data); + $this->assertEqual($result['spamResult'], 'SPAM', t('Partial match was blocked.')); // Update the blacklist entry to match the term only exactly. - $data = array( + $entry = $mollom->updateBlacklistEntry(array( + 'id' => $entry['id'], 'text' => $term, 'context' => 'everything', 'reason' => 'spam', 'match' => 'exact', - ); - $result = mollom('mollom.addBlacklistText', $data); - $this->assertTrue($result, t('The text was blacklisted.')); + )); + $this->assertTrue($entry['id'], t('The blacklist entry was updated.')); $data = array( - 'post_body' => "When match is 'exact', it has to be exact: " . $term, + 'postBody' => "When match is 'exact', it has to be exact: " . $term, ); - $result = mollom('mollom.checkContent', $data); - $this->assertEqual($result['spam'], MOLLOM_ANALYSIS_SPAM, t('Exact match was blocked.')); + $result = mollom()->checkContent($data); + $this->assertEqual($result['spamResult'], 'SPAM', t('Exact match was blocked.')); $data = array( - 'post_body' => "When match is 'exact', it has to be exact: abc{$term}def", + 'postBody' => "When match is 'exact', it has to be exact: abc{$term}def", ); - $result = mollom('mollom.checkContent', $data); - $this->assertNotEqual($result['spam'], MOLLOM_ANALYSIS_SPAM, t('Partial match was not blocked.')); + $result = mollom()->checkContent($data); + $this->assertNotEqual($result['spamResult'], 'SPAM', t('Partial match was not blocked.')); - $data = array( - 'text' => $term, - 'context' => 'everything', - 'reason' => 'spam', - ); - $result = mollom('mollom.removeBlacklistText', $data); - $this->assertTrue($result, t('The blacklisted text was removed.')); + $result = $mollom->deleteBlacklistEntry($entry['id']); + $this->assertIdentical($result, TRUE, t('The blacklisted text was removed.')); // Try to remove a non-existing entry. - $data = array( - 'text' => $term, - 'context' => 'everything', - 'reason' => 'spam', - ); - $result = mollom('mollom.removeBlacklistText', $data); + // @todo Ensure that the ID does not exist. + $result = $mollom->deleteBlacklistEntry(999); $this->assertMollomWatchdogMessages(WATCHDOG_EMERGENCY); $this->assertNotIdentical($result, TRUE, t('Error response for a non-existing blacklist text found.')); } @@ -1670,7 +1700,7 @@ class MollomProfanityTestCase extends MollomWebTestCase { $this->drupalPost(NULL, $edit, t('Save')); $this->assertText($this->profanity_message); $this->assertNoText(t('Your comment has been posted.')); - $session_id = $this->assertSessionIDInForm(); + $contentId = $this->assertResponseIDInForm('contentId'); $edit["comment_body[$langcode][0][value]"] = 'not profane ham'; $this->drupalPost(NULL, $edit, t('Save')); @@ -1678,10 +1708,11 @@ class MollomProfanityTestCase extends MollomWebTestCase { $this->assertText(t('Your comment has been posted.')); $this->assertRaw('
' . $edit["comment_body[$langcode][0][value]"] . '
', t('Comment previously containing profanity was found.')); $cid = db_query('SELECT cid FROM {comment} WHERE subject = :subject ORDER BY created DESC', array(':subject' => $edit['subject']))->fetchField(); - $this->assertMollomData('comment', $cid, $session_id); + $this->assertMollomData('comment', $cid, 'contentId', $contentId); // Sequence: Post unsure spam (not profanity), post profanity along with // correct CAPTCHA, and expect that to be discarded. + $this->resetResponseID(); $this->web_user = $this->drupalCreateUser(); $this->drupalLogin($this->web_user); $edit = array( @@ -1696,7 +1727,8 @@ class MollomProfanityTestCase extends MollomWebTestCase { $this->assertCaptchaField(); $this->assertNoText($this->profanity_message); $this->assertNoText(t('Your comment has been posted.')); - $session_id = $this->assertSessionIDInForm(); + $contentId = $this->assertResponseIDInForm('contentId'); + $captchaId = $this->assertResponseIDInForm('captchaId'); $edit["comment_body[$langcode][0][value]"] = 'unsure profanity'; $this->postCorrectCaptcha(NULL, $edit, t('Save')); @@ -1710,6 +1742,9 @@ class MollomProfanityTestCase extends MollomWebTestCase { * Tests Mollom form configuration functionality. */ class MollomFormConfigurationTestCase extends MollomWebTestCase { + // Re-route Mollom communication to this testing site. + protected $mollomClass = 'MollomDrupalTestLocal'; + public static function getInfo() { return array( 'name' => 'Form administration', @@ -1719,9 +1754,7 @@ class MollomFormConfigurationTestCase extends MollomWebTestCase { } function setUp() { - parent::setUp(array('mollom_test')); - // Re-route Mollom communication to this testing site. - variable_set('mollom_servers', array($GLOBALS['base_url'] . '/xmlrpc.php?version=')); + parent::setUp(); $this->drupalLogin($this->admin_user); } @@ -2176,7 +2209,7 @@ class MollomNodeFormTestCase extends MollomWebTestCase { // Login and submit a node. $this->drupalLogin($this->web_user); $this->drupalGet('node/add/article'); - $session_id = $this->assertSessionIDInForm(); + $captchaId = $this->assertResponseIDInForm('captchaId'); $edit = array( 'title' => 'spam', 'mollom[captcha]' => 'correct', @@ -2184,7 +2217,7 @@ class MollomNodeFormTestCase extends MollomWebTestCase { $this->drupalPost(NULL, $edit, t('Save')); $this->node = $this->drupalGetNodeByTitle($edit['title']); $this->assertUrl('node/' . $this->node->nid); - $this->assertMollomData('node', $this->node->nid, $session_id); + $this->assertMollomData('node', $this->node->nid, 'captchaId', $captchaId); } } @@ -2240,21 +2273,21 @@ class MollomCommentFormTestCase extends MollomWebTestCase { $this->drupalLogin($this->web_user); $this->drupalGet('comment/reply/' . $this->node->nid); $this->assertCaptchaField(); - $this->assertSessionIDInForm(); + $this->assertResponseIDInForm('captchaId'); $this->assertNoPrivacyLink(); // Try to submit an incorrect answer for the CAPTCHA, without value for // required field. $this->postIncorrectCaptcha(NULL, array(), t('Preview')); $this->assertText(t('Comment field is required.')); - $this->assertSessionIDInForm(); + $this->assertResponseIDInForm('captchaId'); $this->assertNoPrivacyLink(); // Try to submit a correct answer for the CAPTCHA, still without required // field value. $this->postCorrectCaptcha(NULL, array(), t('Preview')); $this->assertText(t('Comment field is required.')); - $session_id = $this->assertSessionIDInForm(); + $captchaId = $this->assertResponseIDInForm('captchaId'); $this->assertNoPrivacyLink(); // Finally, we should be able to submit a comment. @@ -2262,13 +2295,14 @@ class MollomCommentFormTestCase extends MollomWebTestCase { $this->assertText(t('Your comment has been posted.')); $this->assertRaw('spam
', t('Spam comment could be posted with correct CAPTCHA.')); $cid = db_query('SELECT cid FROM {comment} WHERE subject = :subject ORDER BY created DESC', array(':subject' => 'spam'))->fetchField(); - $this->assertMollomData('comment', $cid, $session_id); + $this->assertMollomData('comment', $cid, 'captchaId', $captchaId); // Verify we can solve the CAPTCHA directly. + $this->resetResponseID(); $value = 'some more spam'; $this->drupalGet('comment/reply/' . $this->node->nid); $this->assertCaptchaField(); - $session_id = $this->assertSessionIDInForm(); + $captchaId = $this->assertResponseIDInForm('captchaId'); $edit = array( 'comment_body[und][0][value]' => $value, 'mollom[captcha]' => 'correct', @@ -2276,7 +2310,7 @@ class MollomCommentFormTestCase extends MollomWebTestCase { $this->drupalPost(NULL, $edit, t('Save')); $this->assertText(t('Your comment has been posted.')); $cid = db_query('SELECT cid FROM {comment} WHERE subject = :subject ORDER BY created DESC', array(':subject' => $value))->fetchField(); - $this->assertMollomData('comment', $cid, $session_id); + $this->assertMollomData('comment', $cid, 'captchaId', $captchaId); } /** @@ -2300,7 +2334,7 @@ class MollomCommentFormTestCase extends MollomWebTestCase { ); $this->drupalPost(NULL, $edit, t('Save')); $this->assertCaptchaField(); - $session_id = $this->assertSessionIDInForm(); + $contentId = $this->assertResponseIDInForm('contentId'); $this->assertPrivacyLink(); // Try to submit the form by solving the CAPTCHA incorrectly. At this point, @@ -2308,34 +2342,34 @@ class MollomCommentFormTestCase extends MollomWebTestCase { // the comment is still neither ham or spam. $this->postIncorrectCaptcha(NULL, array(), t('Save')); $this->assertCaptchaField(); - $session_id = $this->assertSessionIDInForm(); + $captchaId = $this->assertResponseIDInForm('captchaId'); $this->assertPrivacyLink(); // Correctly solving the CAPTCHA should accept the form submission. $this->postCorrectCaptcha(NULL, array(), t('Save')); $this->assertRaw('' . $edit['comment_body[und][0][value]'] . '
', t('A comment that may contain spam was found.')); $cid = db_query('SELECT cid FROM {comment} WHERE subject = :subject ORDER BY created DESC', array(':subject' => $edit['comment_body[und][0][value]']))->fetchField(); - $this->assertMollomData('comment', $cid, $session_id); + $this->assertMollomData('comment', $cid, 'contentId', $contentId); // Try to save a new 'spam' comment; it should be discarded, with no CAPTCHA // appearing on the page. - $this->resetSessionID(); + $this->resetResponseID(); $this->drupalGet('comment/reply/' . $this->node->nid); $this->assertPrivacyLink(); $original_number_of_comments = $this->getCommentCount($this->node->nid); $this->assertSpamSubmit(NULL, array('comment_body[und][0][value]'), array(), t('Save')); - $session_id = $this->assertSessionIDInForm(); + $contentId = $this->assertResponseIDInForm('contentId'); $this->assertCommentCount($this->node->nid, $original_number_of_comments); $this->assertPrivacyLink(); // Try to save again; it should be discarded, with no CAPTCHA. $this->assertSpamSubmit(NULL, array('comment_body[und][0][value]'), array(), t('Save')); - $session_id = $this->assertSessionIDInForm(); + $contentId = $this->assertResponseIDInForm('contentId'); $this->assertCommentCount($this->node->nid, $original_number_of_comments); $this->assertPrivacyLink(); // Save a new 'ham' comment. - $this->resetSessionID(); + $this->resetResponseID(); $this->drupalGet('comment/reply/' . $this->node->nid); $this->assertPrivacyLink(); $original_number_of_comments = $this->getCommentCount($this->node->nid); @@ -2529,7 +2563,7 @@ class MollomResellerTestCase extends MollomWebTestCase { // Create 3 test sites: for ($i = 1; $i <= 3; $i++) { - $keys[] = mollom('mollom.createSite', array( + $keys[] = mollom()->createSite(array( 'url' => 'http://example.com/site-'. $i, 'mail' => 'mail@example.com', 'status' => 0, @@ -2543,7 +2577,7 @@ class MollomResellerTestCase extends MollomWebTestCase { $sites = mollom('mollom.listSites'); foreach ($sites as $site) { // Retrieve the site information: - $details = mollom('mollom.getSite', array('client_key' => $site)); + $details = mollom()->getSite(array('client_key' => $site)); $this->assertEqual($details['mail'], 'mail@example.com', t('The original information is correctly retrieved from Mollom.')); $this->assertEqual($details['status'], 0, t('The original information is correctly retrieved from Mollom.')); @@ -2553,8 +2587,8 @@ class MollomResellerTestCase extends MollomWebTestCase { // valid sites in case someone messed up their Mollom settings! if ($details['mail'] == 'mail@example.com' || $details['mail'] == 'root@example.com') { // Update the information on the site and verify that it was updated. - mollom('mollom.updateSite', array('client_key' => $site, 'mail' => 'root@example.com')); - $details = mollom('mollom.getSite', array('client_key' => $site)); + mollom()->updateSite(array('client_key' => $site, 'mail' => 'root@example.com')); + $details = mollom()->getSite(array('client_key' => $site)); $this->assertEqual($details['mail'], 'root@example.com', t('The updated information is correctly retrieved from Mollom.')); // Verify that the existing information did not change (partial updates). @@ -2562,7 +2596,7 @@ class MollomResellerTestCase extends MollomWebTestCase { $this->assertEqual($details['testing'], 1, t('The original information is correctly retrieved from Mollom.')); // Delete the test site: - mollom('mollom.deleteSite', array('client_key' => $site)); + mollom()->deleteSite(array('client_key' => $site)); } else { $this->fail(t('We tried to delete a non-test site.')); @@ -2573,7 +2607,7 @@ class MollomResellerTestCase extends MollomWebTestCase { $this->assertMollomWatchdogMessages(); // Retrieve information about a non-existing site: - $details = mollom('mollom.getSite', array('client_key' => 'bogus')); + $details = mollom()->getSite(array('client_key' => 'bogus')); $this->assertEqual(xmlrpc_errno(), TRUE, t('Retrieving information from a non-existing site returned an XML-RPC error.')); $this->assertMollomWatchdogMessages(WATCHDOG_EMERGENCY); @@ -2587,6 +2621,9 @@ class MollomResellerTestCase extends MollomWebTestCase { * Tests form value processing. */ class MollomDataTestCase extends MollomWebTestCase { + // Re-route Mollom communication to this testing site. + protected $mollomClass = 'MollomDrupalTestLocal'; + public static function getInfo() { return array( 'name' => 'Data processing', @@ -2595,13 +2632,6 @@ class MollomDataTestCase extends MollomWebTestCase { ); } - function setUp() { - // Enable testing server implementation. - parent::setUp(array('mollom_test')); - // Re-route Mollom communication to this testing site. - variable_set('mollom_servers', array($GLOBALS['base_url'] . '/xmlrpc.php?version=')); - } - /** * Test mollom_form_get_values(). */ @@ -2640,14 +2670,14 @@ class MollomDataTestCase extends MollomWebTestCase { ); $data = mollom_form_get_values($values, $fields, $form_info['mapping']); - $this->assertSame('post_title', $data['post_title'], $values['subject']); - $this->assertSame('post_body', $data['post_body'], $values['message'] . "\n" . $values['parent']['child']); - $this->assertSame('author_name', $data['author_name'], $values['name']); - $this->assertSame('author_mail', $data['author_mail'], $values['mail']); - $this->assertFalse(isset($data['author_url']), t('author_url: Undefined.')); - $this->assertFalse(isset($data['author_openid']), t('author_openid: Undefined.')); - $this->assertFalse(isset($data['author_id']), t('author_id: Undefined.')); - $this->assertSame('author_ip', $data['author_ip'], ip_address()); + $this->assertSame('postTitle', $data['postTitle'], $values['subject']); + $this->assertSame('postBody', $data['postBody'], $values['message'] . "\n" . $values['parent']['child']); + $this->assertSame('authorName', $data['authorName'], $values['name']); + $this->assertSame('authorMail', $data['authorMail'], $values['mail']); + $this->assertFalse(isset($data['authorUrl']), t('authorUrl: Undefined.')); + $this->assertFalse(isset($data['authorOpenid']), t('authorOpenid: Undefined.')); + $this->assertFalse(isset($data['authorId']), t('authorId: Undefined.')); + $this->assertSame('authorIp', $data['authorIp'], ip_address()); // Verify submitted form values for an registered user. $values = array( @@ -2657,15 +2687,15 @@ class MollomDataTestCase extends MollomWebTestCase { ); $data = mollom_form_get_values($values, $fields, $form_info['mapping']); - $this->assertSame('post_title', $data['post_title'], $values['subject']); - $this->assertSame('post_body', $data['post_body'], $values['message']); - $this->assertSame('author_name', $data['author_name'], $this->admin_user->name); - $this->assertSame('author_mail', $data['author_mail'], $this->admin_user->mail); - $this->assertFalse(isset($data['author_url']), t('author_url: Undefined.')); + $this->assertSame('postTitle', $data['postTitle'], $values['subject']); + $this->assertSame('postBody', $data['postBody'], $values['message']); + $this->assertSame('authorName', $data['authorName'], $this->admin_user->name); + $this->assertSame('authorMail', $data['authorMail'], $this->admin_user->mail); + $this->assertFalse(isset($data['authorUrl']), t('authorUrl: Undefined.')); // @todo Test this. - $this->assertFalse(isset($data['author_openid']), t('author_openid: Undefined.')); - $this->assertSame('author_id', $data['author_id'], $this->admin_user->uid); - $this->assertSame('author_ip', $data['author_ip'], ip_address()); + $this->assertFalse(isset($data['authorOpenid']), t('authorOpenid: Undefined.')); + $this->assertSame('authorId', $data['authorId'], $this->admin_user->uid); + $this->assertSame('authorIp', $data['authorIp'], ip_address()); } /** @@ -2702,11 +2732,11 @@ class MollomDataTestCase extends MollomWebTestCase { // Verify that submitted data equals post data. $data = $this->getServerRecord(); - $this->assertSame('post_title', $data['post_title'], $edit['subject']); - $this->assertSame('post_body', $data['post_body'], $edit['comment_body[und][0][value]']); - $this->assertSame('author_name', $data['author_name'], $this->web_user->name); - $this->assertSame('author_mail', $data['author_mail'], $this->web_user->mail); - $this->assertSame('author_id', $data['author_id'], $this->web_user->uid); + $this->assertSame('postTitle', $data['postTitle'], $edit['subject']); + $this->assertSame('postBody', $data['postBody'], $edit['comment_body[und][0][value]']); + $this->assertSame('authorName', $data['authorName'], $this->web_user->name); + $this->assertSame('authorMail', $data['authorMail'], $this->web_user->mail); + $this->assertSame('authorId', $data['authorId'], $this->web_user->uid); $this->assertSame('strictness', $data['strictness'], 'normal'); $this->PostCorrectCaptcha(NULL, array(), t('Save')); @@ -2715,7 +2745,7 @@ class MollomDataTestCase extends MollomWebTestCase { // Verify that submitted data equals post data. $data = $this->getServerRecord('mollom.checkCaptcha'); - $this->assertSame('author_id', $data['author_id'], $this->web_user->uid); + $this->assertSame('authorId', $data['authorId'], $this->web_user->uid); // Allow anonymous users to post comments without approval. $this->drupalLogin($this->admin_user); @@ -2750,12 +2780,12 @@ class MollomDataTestCase extends MollomWebTestCase { // Verify that submitted data equals post data. $data = $this->getServerRecord(); - $this->assertSame('post_title', $data['post_title'], $edit['subject']); - $this->assertSame('post_body', $data['post_body'], $edit['comment_body[und][0][value]']); - $this->assertSame('author_name', $data['author_name'], $edit['name']); - $this->assertSame('author_mail', $data['author_mail'], $edit['mail']); - $this->assertSame('author_url', $data['author_url'], $edit['homepage']); - $this->assertFalse(isset($data['author_id']), t('author_id: Undefined.')); + $this->assertSame('postTitle', $data['postTitle'], $edit['subject']); + $this->assertSame('postBody', $data['postBody'], $edit['comment_body[und][0][value]']); + $this->assertSame('authorName', $data['authorName'], $edit['name']); + $this->assertSame('authorMail', $data['authorMail'], $edit['mail']); + $this->assertSame('authorUrl', $data['authorUrl'], $edit['homepage']); + $this->assertFalse(isset($data['authorId']), t('authorId: Undefined.')); $this->PostCorrectCaptcha(NULL, array(), t('Save')); $comment = db_query('SELECT * FROM {comment} WHERE subject = :subject', array(':subject' => $edit['subject']))->fetchObject(); @@ -2763,7 +2793,7 @@ class MollomDataTestCase extends MollomWebTestCase { // Verify that submitted data equals post data. $data = $this->getServerRecord('mollom.checkCaptcha'); - $this->assertFalse(isset($data['author_id']), t('author_id: Undefined.')); + $this->assertFalse(isset($data['authorId']), t('authorId: Undefined.')); // Log in admin user and edit comment containing spam. $this->resetServerRecords(); @@ -2879,13 +2909,16 @@ class MollomDataTestCase extends MollomWebTestCase { $edit['title'] = 'unsure'; $this->drupalPost(NULL, $edit, 'Submit'); $this->assertCaptchaField(); + $contentId = $this->assertResponseIDInForm('contentId'); + $captchaId = $this->assertResponseIDInForm('captchaId'); $this->postCorrectCaptcha(NULL, array(), 'Submit', 'Successful form submission.'); $new_data = $this->assertMollomData('mollom_test', $mid); // Verify that only session data was updated. $this->assertSame('entity', $data->entity, $new_data->entity); $this->assertSame('id', $data->id, $new_data->id); - $this->assertNotSame('session_id', $data->session_id, $new_data->session_id); + $this->assertNotSame('contentId', $data->contentId, $new_data->contentId); + $this->assertNotSame('captchaId', $data->captchaId, $new_data->captchaId); $this->assertSame('form_id', $data->form_id, $new_data->form_id); $this->assertSame('quality', $data->quality, $new_data->quality); $count = db_query("SELECT COUNT(1) FROM {mollom}")->fetchField(); @@ -2901,12 +2934,12 @@ class MollomDataTestCase extends MollomWebTestCase { // Verify that we additionally sent version data. $data = $this->getServerRecord('mollom.verifyKey'); - $info = _mollom_get_version(); - $this->assertTrue(!empty($info['platform_name']), t('Version information found.')); - $this->assertSame('platform_name', $data['platform_name'], $info['platform_name']); - $this->assertSame('platform_version', $data['platform_version'], $info['platform_version']); - $this->assertSame('client_name', $data['client_name'], $info['client_name']); - $this->assertSame('client_version', $data['client_version'], $info['client_version']); + $info = mollom()->getClientInformation(); + $this->assertTrue(!empty($info['platformName']), t('Version information found.')); + $this->assertSame('platformName', $data['platformName'], $info['platformName']); + $this->assertSame('platformVersion', $data['platformVersion'], $info['platformVersion']); + $this->assertSame('clientName', $data['clientName'], $info['clientName']); + $this->assertSame('clientVersion', $data['clientVersion'], $info['clientVersion']); } } @@ -2937,28 +2970,28 @@ class MollomDataCRUDTestCase extends MollomWebTestCase { 'entity' => 'type1', 'id' => 123, 'form_id' => 'type1_form', - 'session_id' => 'type1-session-id', + 'contentId' => 1, ); mollom_data_save($data1); - $this->assertMollomData($data1->entity, $data1->id, $data1->session_id); + $this->assertMollomData($data1->entity, $data1->id, 'contentId', $data1->contentId); // Create a second data record; same ID, different entity type. $data2 = (object) array( 'entity' => 'type2', 'id' => 123, 'form_id' => 'type2_form', - 'session_id' => 'type2-session-id', + 'contentId' => 2, ); mollom_data_save($data2); - $this->assertMollomData($data2->entity, $data2->id, $data2->session_id); + $this->assertMollomData($data2->entity, $data2->id, 'contentId', $data2->contentId); // Update the first data record. - $data1->session_id = 'new-session-id-type1'; + $data1->contentId = 3; mollom_data_save($data1); // Verify that both records are correct. - $this->assertMollomData($data1->entity, $data1->id, $data1->session_id); - $this->assertMollomData($data2->entity, $data2->id, $data2->session_id); + $this->assertMollomData($data1->entity, $data1->id, 'contentId', $data1->contentId); + $this->assertMollomData($data2->entity, $data2->id, 'contentId', $data2->contentId); } /** @@ -2970,7 +3003,7 @@ class MollomDataCRUDTestCase extends MollomWebTestCase { 'entity' => 'type1', 'id' => 123, 'form_id' => 'type1_form', - 'session_id' => 'type1-session-id', + 'contentId' => 1, ); mollom_data_save($data1); @@ -2979,20 +3012,20 @@ class MollomDataCRUDTestCase extends MollomWebTestCase { 'entity' => 'type2', 'id' => 123, 'form_id' => 'type2_form', - 'session_id' => 'type2-session-id', + 'contentId' => 2, ); mollom_data_save($data2); // Verify that both records exist. - $this->assertMollomData($data1->entity, $data1->id, $data1->session_id); - $this->assertMollomData($data2->entity, $data2->id, $data2->session_id); + $this->assertMollomData($data1->entity, $data1->id, 'contentId', $data1->contentId); + $this->assertMollomData($data2->entity, $data2->id, 'contentId', $data2->contentId); // Delete the first data record. mollom_data_delete($data1->entity, $data1->id); // Verify that only the second record remained and was not changed. $this->assertNoMollomData($data1->entity, $data1->id); - $this->assertMollomData($data2->entity, $data2->id, $data2->session_id); + $this->assertMollomData($data2->entity, $data2->id, 'contentId', $data2->contentId); } } @@ -3055,7 +3088,7 @@ class MollomAnalysisTestCase extends MollomWebTestCase { $data = $this->assertMollomData('mollom_test', $mid); $record = mollom_test_load($mid); $this->assertEqual($record['status'], 0, t('Unpublished test post found.')); - $this->assertSame('spam', $data->spam, MOLLOM_ANALYSIS_SPAM); + $this->assertSame('spam', $data->spam, 1.0); $this->assertSame('profanity', $data->profanity, 1); $this->assertSame('moderate', $data->moderate, 1); @@ -3070,7 +3103,7 @@ class MollomAnalysisTestCase extends MollomWebTestCase { $data = $this->assertMollomData('mollom_test', $mid); $record = mollom_test_load($mid); $this->assertEqual($record['status'], 0, t('Unpublished test post found.')); - $this->assertSame('spam', $data->spam, MOLLOM_ANALYSIS_SPAM); + $this->assertSame('spam', $data->spam, 1.0); $this->assertSame('profanity', $data->profanity, 1); $this->assertSame('moderate', $data->moderate, 1); @@ -3084,7 +3117,7 @@ class MollomAnalysisTestCase extends MollomWebTestCase { $data = $this->assertMollomData('mollom_test', $mid); $record = mollom_test_load($mid); $this->assertEqual($record['status'], 1, t('Published test post found.')); - $this->assertSame('spam', $data->spam, MOLLOM_ANALYSIS_SPAM); + $this->assertSame('spam', $data->spam, 1.0); $this->assertSame('profanity', $data->profanity, 1); $this->assertSame('moderate', $data->moderate, 0); @@ -3092,9 +3125,9 @@ class MollomAnalysisTestCase extends MollomWebTestCase { // marked for moderation. $this->drupalLogout(); $expectations = array( - 'ham' => array('spam' => MOLLOM_ANALYSIS_HAM, 'profanity' => 0), - 'unsure' => array('spam' => MOLLOM_ANALYSIS_UNSURE, 'profanity' => 0), - $this->randomString() => array('spam' => MOLLOM_ANALYSIS_UNSURE, 'profanity' => 0), + 'ham' => array('spam' => 0.0, 'spamResult' => 'HAM', 'profanity' => 0), + 'unsure' => array('spam' => 0.5, 'spamResult' => 'UNSURE', 'profanity' => 0), + $this->randomString() => array('spam' => 0.5, 'spamResult' => 'UNSURE', 'profanity' => 0), ); foreach ($expectations as $body => $expected) { $edit = array( @@ -3102,7 +3135,7 @@ class MollomAnalysisTestCase extends MollomWebTestCase { 'body' => $body, ); $this->drupalPost('mollom-test/form', $edit, 'Submit'); - if ($expected['spam'] == MOLLOM_ANALYSIS_UNSURE) { + if ($expected['spamResult'] == 'UNSURE') { $this->postCorrectCaptcha(NULL, array(), 'Submit'); } $mid = $this->assertTestSubmitData(); diff --git a/tests/mollom_test.install b/tests/mollom_test.install index b4bea98..8c3e9a1 100644 --- a/tests/mollom_test.install +++ b/tests/mollom_test.install @@ -42,3 +42,12 @@ function mollom_test_schema() { return $schema; } +/** + * Implements hook_uninstall(). + */ +function mollom_test_uninstall() { + db_delete('variable') + ->condition('name', db_like('mollom_test_') . '%', 'LIKE') + ->execute(); +} + diff --git a/tests/mollom_test.module b/tests/mollom_test.module index d8d9192..9fd14ff 100644 --- a/tests/mollom_test.module +++ b/tests/mollom_test.module @@ -3,6 +3,16 @@ /** * @file * Testing functionality for Mollom module. + * + * @todo Extract testing server into a new mollom_test_server.module. The + * mollom_test.module serves as good example for how to implement Mollom + * support in a Drupal module, but 90% of it pertain to the testing server + * now, so it's hard to explain people what they should look at. + */ + +/** + * @defgroup mollom_test_xmlrpc Mollom XML-RPC fake server functions + * @{ */ /** @@ -12,28 +22,456 @@ function mollom_test_xmlrpc() { return array( // $data contains a variable amount of properties, so we cannot specify a // signature. - 'mollom.getServerList' => 'mollom_test_get_server_list', - 'mollom.verifyKey' => 'mollom_test_verify_key', - 'mollom.checkContent' => 'mollom_test_check_content', - 'mollom.getImageCaptcha' => 'mollom_test_get_captcha', - 'mollom.checkCaptcha' => 'mollom_test_check_captcha', - 'mollom.sendFeedback' => 'mollom_test_send_feedback', + 'mollom.getServerList' => 'mollom_test_xmlrpc_get_server_list', + 'mollom.verifyKey' => 'mollom_test_xmlrpc_verify_key', + 'mollom.checkContent' => 'mollom_test_xmlrpc_check_content', + 'mollom.getImageCaptcha' => 'mollom_test_xmlrpc_get_captcha', + 'mollom.checkCaptcha' => 'mollom_test_xmlrpc_check_captcha', + 'mollom.sendFeedback' => 'mollom_test_xmlrpc_send_feedback', ); } /** + * Converts camelCase request/response parameters to lowercase with underscores. + * + * @todo Recurse into multi-dimensional arrays. + */ +function mollom_test_xmlrpc_convert_params(array $data = array()) { + foreach ($data as $key => $value) { + // Convert CamelCase to lowercase with underscores. + $new_key = strtolower(preg_replace('@(?<=[a-z])([A-Z])@', '_$1', $key)); + $data[$new_key] = $value; + unset($data[$key]); + } + return $data; +} + +/** * XML-RPC callback for mollom.getServerList to retrieve new server list. */ +function mollom_test_xmlrpc_get_server_list($data) { + $servers = mollom_test_get_server_list($data); + foreach ($servers as $key => $url) { + $servers[$key] .= '/xmlrpc.php?version='; + } + return $servers; +} + +/** + * XML-RPC callback for mollom.verifyKey to validate API keys. + */ +function mollom_test_xmlrpc_verify_key($data) { + $valid = mollom_test_verify_key($data); + if ($valid) { + return TRUE; + } + xmlrpc_error(Mollom::AUTH_ERROR); +} + +/** + * XML-RPC callback for mollom.checkContent to perform textual analysis. + */ +function mollom_test_xmlrpc_check_content($data) { + return mollom_test_check_content($data); +} + +/** + * XML-RPC callback for mollom.getImageCaptcha to fetch a CATPCHA image. + */ +function mollom_test_xmlrpc_get_captcha($data) { + return mollom_test_get_captcha($data); +} + +/** + * XML-RPC callback for mollom.checkCaptcha to validate a CAPTCHA response. + */ +function mollom_test_xmlrpc_check_captcha($data) { + if (isset($data['captcha_result'])) { + $data['solution'] = $data['captcha_result']; + } + + return mollom_test_check_captcha($data); +} + +/** + * XML-RPC callback for mollom.sendFeedback to send feedback for a moderated post. + */ +function mollom_test_xmlrpc_send_feedback($data) { + $result = mollom_test_send_feedback($data); + if ($result) { + return TRUE; + } + xmlrpc_error(Mollom::AUTH_ERROR); +} + +/** + * @} End of "defgroup mollom_test_xmlrpc". + */ + +/** + * @defgroup mollom_test_rest Mollom REST fake server functions + * @{ + */ + +/** + * Implements hook_menu() for REST API endpoints. + */ +function mollom_test_rest() { + $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 callback' => TRUE, + 'type' => MENU_CALLBACK, + 'delivery callback' => 'mollom_test_rest_deliver', + ); + + $items[$path . '/site/%'] = $base + array( + 'page callback' => 'mollom_test_rest_site', + 'page arguments' => array($base_args + 2), + ); + + $items[$path . '/content'] = $base + array( + 'page callback' => 'mollom_test_rest_content', + ); + $items[$path . '/content/%'] = $base + array( + 'page callback' => 'mollom_test_rest_content', + 'page arguments' => array($base_args + 2), + ); + + $items[$path . '/captcha'] = $base + array( + 'page callback' => 'mollom_test_rest_captcha', + ); + $items[$path . '/captcha/%'] = $base + array( + 'page callback' => 'mollom_test_rest_captcha', + 'page arguments' => array($base_args + 2), + ); + + $items[$path . '/content/%/feedback'] = $base + array( + 'page callback' => 'mollom_test_rest_send_feedback', + 'page arguments' => array($base_args + 2), + ); + + $items[$path . '/blacklist/%'] = $base + array( + 'page callback' => 'mollom_test_rest_blacklist', + 'page arguments' => array($base_args + 2), + ); + $items[$path . '/blacklist/%/%'] = $base + array( + 'page callback' => 'mollom_test_rest_blacklist', + 'page arguments' => array($base_args + 2, $base_args + 3), + ); + // @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_rest_get_parameters() { + if ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD') { + $data = Mollom::httpParseQuery($_SERVER['QUERY_STRING']); + } + elseif ($_SERVER['REQUEST_METHOD'] == 'POST' || $_SERVER['REQUEST_METHOD'] == 'PUT') { + $data = Mollom::httpParseQuery(file_get_contents('php://input')); + } + return $data; +} + +/** + * Delivery callback for REST API endpoints. + */ +function mollom_test_rest_deliver($page_callback_result) { + #drupal_add_http_header('Content-Type', 'application/xml; charset=utf-8'); + drupal_add_http_header('Content-Type', 'application/xml'); + + $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. + // @todo Add support for HTTP/resource errors; e.g., 404, 403, etc. + if (!isset($page_callback_result)) { + $page_callback_result = FALSE; + } + $status = array( + 'code' => $page_callback_result === FALSE ? 1 : 0, + 'message' => '', + ); + mollom_test_rest_add_xml($xml, $element, $status); + + // Append other response parameters. + if ($page_callback_result !== FALSE) { + mollom_test_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_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_rest_add_xml($doc, $element, $value, $key); + } + } +} + +/** + * REST callback for CRUD site operations. + * + * @param $public_key + * The public key of a site. Has to be MOLLOM_TEST_PUBLIC_KEY. + */ +function mollom_test_rest_site($id) { + $data = mollom_test_rest_get_parameters(); + if ($_SERVER['REQUEST_METHOD'] == 'PUT') { + mollom_test_verify_key($data); + } + else { + module_load_include('php', 'simpletest', 'drupal_web_test_case'); + module_load_include('test', 'mollom', 'tests/mollom'); + } + // Verify keys (in case of PUT, again). + if ($data['publicKey'] !== MOLLOM_TEST_PUBLIC_KEY) { + return FALSE; + } + + $version = mollom()->getClientInformation(); + $servers = mollom_test_get_server_list($data); + foreach ($servers as $key => &$url) { + $url .= '/mollom-test/rest'; + } + + $response = array( + 'publicKey' => MOLLOM_TEST_PUBLIC_KEY, + 'privateKey' => MOLLOM_TEST_PRIVATE_KEY, + 'url' => $GLOBALS['base_url'], + 'email' => variable_get('site_mail', ''), + 'languages' => array(), + 'subscriptionType' => 'free', + 'platform' => array( + 'name' => $version['platformName'], + 'version' => $version['platformVersion'], + ), + 'client' => array( + 'name' => $version['clientName'], + 'version' => $version['clientVersion'], + ), + 'servers' => $servers, + // @todo Statistics. + ); + return array('site' => $response); +} + +/** + * REST callback for mollom.checkContent to perform textual analysis. + */ +function mollom_test_rest_content($contentId = NULL) { + $data = mollom_test_rest_get_parameters(); + if ($_SERVER['REQUEST_METHOD'] == 'GET') { + // @todo List/read content. + if (empty($contentId)) { + return FALSE; + } + return FALSE; + } + elseif ($_SERVER['REQUEST_METHOD'] == 'PUT') { + // Update existing content (includes sending feedback). + // In case the 'moderated' parameter was passed, the call equals the old + // mollom.sendFeedback and we only check whether the parameter value is + // correct. + if (isset($data['moderated'])) { + $valid = is_string($data['moderated']); + $valid = $valid && in_array($data['moderated'], array('spam', 'profanity', 'low-quality', 'unwanted', 'approve', 'escalate', 'delete', 'ignore')); + return $valid; + } + if (empty($contentId)) { + return FALSE; + } + if (isset($data['contentId']) && $data['contentId'] != $contentId) { + return FALSE; + } + } + + // Default: Create (POST) or update (PUT) content and check it. + return array('content' => mollom_test_check_content($data)); +} + +/** + * REST callback for mollom.getCaptcha to fetch a CAPTCHA. + */ +function mollom_test_rest_captcha($captchaId = NULL) { + $data = mollom_test_rest_get_parameters(); + if ($_SERVER['REQUEST_METHOD'] == 'GET') { + // @todo List/read content. + if (empty($captchaId)) { + return FALSE; + } + return FALSE; + } + elseif ($_SERVER['REQUEST_METHOD'] == 'PUT') { + if (empty($captchaId)) { + return FALSE; + } + if (isset($data['captchaId']) && $data['captchaId'] != $captchaId) { + return FALSE; + } + return array('captcha' => mollom_test_check_captcha($data)); + } + + // POST. + return array('captcha' => mollom_test_get_captcha($data)); +} + +/** + * REST callback for Blacklist API. + * + * @todo Abstract actual functionality like other REST handlers. + */ +function mollom_test_rest_blacklist($siteId, $entryId = NULL, $delete = FALSE) { + if (empty($siteId)) { + return FALSE; + } + $data = mollom_test_rest_get_parameters(); + // Remove authentication parameters. + unset($data['publicKey'], $data['time'], $data['hash'], $data['nonce']); + + // Prepare text value. + if (isset($data['text'])) { + $data['text'] = drupal_strtolower(trim($data['text'])); + } + // @todo API specification/implementation mismatch. + if (isset($data['value'])) { + $data['value'] = drupal_strtolower(trim($data['value'])); + } + + $bin = 'mollom_test_blacklist_' . $siteId; + $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 FALSE; + } + } + } + elseif ($_SERVER['REQUEST_METHOD'] == 'POST') { + // Create a new entry. + if (!$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 FALSE; + } + } + } + elseif ($_SERVER['REQUEST_METHOD'] == 'PUT') { + // Update an existing entry. + if (empty($entryId)) { + return FALSE; + } + if (isset($data['id']) && $data['id'] != $entryId) { + return FALSE; + } + // Check that the entry was not deleted. + if (empty($entries[$entryId])) { + return FALSE; + } + // Entry ID cannot be updated. + unset($data['id']); + $entries[$entryId] = $data; + variable_set($bin, $entries); + $response = $data; + $response['id'] = $entryId; + return array('entry' => $response); + } +} + +/** + * REST callback for mollom.sendFeedback to send feedback for a moderated post. + */ +function mollom_test_rest_send_feedback($session_id) { + $data = $_POST; + $data['session_id'] = $session_id; + + $result = mollom_test_send_feedback($data); + if ($result) { + return TRUE; + } + return FALSE; +} + +/** + * @} End of "defgroup mollom_test_rest". + */ + +/** + * API callback for mollom.getServerList to retrieve new server list. + */ function mollom_test_get_server_list($data) { $storage = variable_get(__FUNCTION__, array()); $storage[] = $data; variable_set(__FUNCTION__, $storage); - return array($GLOBALS['base_url'] . '/xmlrpc.php?version='); + return array($GLOBALS['base_url'], $GLOBALS['base_url']); } /** - * XML-RPC callback for mollom.verifyKey to validate API keys. + * API callback for mollom.verifyKey to validate API keys. */ function mollom_test_verify_key($data) { $storage = variable_get(__FUNCTION__, array()); @@ -44,29 +482,27 @@ function mollom_test_verify_key($data) { module_load_include('php', 'simpletest', 'drupal_web_test_case'); module_load_include('test', 'mollom', 'tests/mollom'); - if ($data['public_key'] === MOLLOM_TEST_PUBLIC_KEY) { - return TRUE; - } - xmlrpc_error(MOLLOM_ERROR); + return $data['public_key'] === MOLLOM_TEST_PUBLIC_KEY; } /** - * XML-RPC callback for mollom.checkContent to perform textual analysis. + * API callback for mollom.checkContent to perform textual analysis. * * @todo Add support for 'redirect' and 'refresh' values. */ function mollom_test_check_content($data) { - $storage = variable_get(__FUNCTION__, array()); - $storage[] = $data; - variable_set(__FUNCTION__, $storage); - $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']); + } + // Spam filter: Check post_title and post_body for ham, spam, or unsure. - if (!isset($data['checks']) || strpos($data['checks'], 'spam') !== FALSE) { + if (!isset($data['checks']) || in_array('spam', $data['checks'])) { $spam = FALSE; $ham = FALSE; - foreach (array('post_title', 'post_body') as $key) { + foreach (array('postTitle', 'postBody') as $key) { if (!isset($data[$key])) { continue; } @@ -85,31 +521,36 @@ function mollom_test_check_content($data) { } } if ($spam && $ham) { - $response['spam'] = MOLLOM_ANALYSIS_UNSURE; + $response['spam'] = 0.5; + $response['spamResult'] = 'UNSURE'; $quality = 0.5; } elseif ($spam) { - $response['spam'] = MOLLOM_ANALYSIS_SPAM; - $quality = 0; + $response['spam'] = 1.0; + $response['spamResult'] = 'SPAM'; + $quality = 0.0; } elseif ($ham) { - $response['spam'] = MOLLOM_ANALYSIS_HAM; - $quality = 1; + $response['spam'] = 0.0; + $response['spamResult'] = 'HAM'; + $quality = 1.0; } else { - $response['spam'] = MOLLOM_ANALYSIS_UNSURE; + $response['spam'] = 0.5; + $response['spamResult'] = 'UNSURE'; $quality = NULL; } // In case a previous spam check was unsure and a CAPTCHA was solved, the // result is supposed to be ham. $captcha_sessions = variable_get('mollom_test_check_captcha_sessions', array()); - if (!empty($data['session_id']) && !empty($captcha_sessions[$data['session_id']])) { - $response['spam'] = MOLLOM_ANALYSIS_HAM; + if (!empty($data['captchaId']) && !empty($captcha_sessions[$data['captchaId']])) { + $response['spam'] = 0.0; + $response['spamResult'] = 'HAM'; } } // Quality filter. - if (!isset($data['checks']) || strpos($data['checks'], 'quality') !== FALSE) { + if (!isset($data['checks']) || in_array('quality', $data['checks'])) { if (isset($quality)) { $response['quality'] = $quality; } @@ -121,9 +562,9 @@ function mollom_test_check_content($data) { } // Profanity filter. - if (isset($data['checks']) && strpos($data['checks'], 'profanity') !== FALSE) { + if (isset($data['checks']) && in_array('profanity', $data['checks'])) { $profanity = 0.0; - foreach (array('post_title', 'post_body') as $key) { + foreach (array('postTitle', 'postBody') as $key) { if (isset($data[$key]) && strpos($data[$key], 'profanity') !== FALSE) { $profanity = 1.0; } @@ -131,80 +572,83 @@ function mollom_test_check_content($data) { $response['profanity'] = $profanity; } - if (!empty($data['session_id'])) { - $response['session_id'] = $data['session_id']; - } - else { - drupal_session_start(); - $response['session_id'] = session_id(); - } + $storage = variable_get(__FUNCTION__, array()); + $contentId = (!empty($data['contentId']) ? (int) $data['contentId'] : max(array_keys($storage)) + 1); + $storage[$contentId] = $data; + $response['contentId'] = $contentId; + variable_set(__FUNCTION__, $storage); return $response; } /** - * XML-RPC callback for mollom.getImageCaptcha to fetch a CATPCHA image. + * API callback for mollom.getImageCaptcha to fetch a CATPCHA image. */ function mollom_test_get_captcha($data) { - $storage = variable_get(__FUNCTION__, array()); - $storage[] = $data; - variable_set(__FUNCTION__, $storage); - - drupal_session_start(); + $response = array(); // 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'; - return array( - 'session_id' => !empty($data['session_id']) ? $data['session_id'] : session_id(), - 'url' => $base_url . '/' . drupal_get_path('module', 'mollom') . '/images/powered-by-mollom-2.gif', - ); + $storage = variable_get(__FUNCTION__, array()); + $captchaId = (!empty($data['captchaId']) ? (int) $data['captchaId'] : max(array_keys($storage)) + 1); + $storage[$captchaId] = $data; + $response['captchaId'] = $captchaId; + variable_set(__FUNCTION__, $storage); + + return $response; } /** - * XML-RPC callback for mollom.checkCaptcha to validate a CAPTCHA response. + * API callback for mollom.checkCaptcha to validate a CAPTCHA response. * * @todo Add support for 'redirect' and 'refresh' values. */ function mollom_test_check_captcha($data) { - $storage = variable_get(__FUNCTION__, array()); - $storage[] = $data; - variable_set(__FUNCTION__, $storage); + $response = array(); - if ($data['captcha_result'] == 'correct') { - $result = TRUE; + if (isset($data['solution']) && $data['solution'] == 'correct') { + $response['solved'] = TRUE; } - if ($data['captcha_result'] == 'incorrect') { - $result = FALSE; + else { + $response['solved'] = FALSE; + $response['reason'] = ''; } + + $storage = variable_get(__FUNCTION__, array()); + $captchaId = (!empty($data['captchaId']) ? (int) $data['captchaId'] : max(array_keys($storage)) + 1); + $storage[$captchaId] = $data; + $response['captchaId'] = $captchaId; + variable_set(__FUNCTION__, $storage); + $captcha_sessions = variable_get('mollom_test_check_captcha_sessions', array()); - $captcha_sessions[$data['session_id']] = $result; + $captcha_sessions[$captchaId] = $response['solved']; variable_set('mollom_test_check_captcha_sessions', $captcha_sessions); - return $result; + return $response; } /** - * XML-RPC callback for mollom.sendFeedback to send feedback for a moderated post. + * API callback for mollom.sendFeedback to send feedback for a moderated post. */ function mollom_test_send_feedback($data) { $storage = variable_get(__FUNCTION__, array()); $storage[] = $data; variable_set(__FUNCTION__, $storage); - if (in_array($data['feedback'], array('spam', 'profanity', 'low-quality', 'unwanted', 'ham'))) { - return TRUE; - } - xmlrpc_error(MOLLOM_ERROR); + return in_array($data['feedback'], array('spam', 'profanity', 'low-quality', 'unwanted', 'ham')); } /** * Implements hook_menu(). */ function mollom_test_menu() { + $items = mollom_test_rest(); + $items['mollom-test/form'] = array( 'title' => 'Mollom test form', 'page callback' => 'drupal_get_form',