add a form to protect, configure already protected forms, or remove the protection.', array( '@add-form-url' => url('admin/settings/mollom/add'), )); } if ($path == 'admin/settings/mollom/blacklist') { return t('Mollom automatically blocks unwanted content, and learns from all participating sites to make its filters better. On top of automatic spam blocking, you can provide custom blacklists.'); } if ($path == 'admin/help#mollom') { $output = '
'; $output = t("Allowing users to react, participate and contribute while still keeping your site's content under control can be a huge challenge. Mollom is a web service that helps you identify content quality and, more importantly, helps you stop spam. When content moderation becomes easier, you have more time and energy to interact with your web community. More information about Mollom is available on the Mollom website or in the Mollom FAQ.", array( '@mollom-website' => 'http://mollom.com', '@mollom-faq' => 'http://mollom.com/faq', ) ); $output .= '
'; $output .= t("Mollom can be used to block all types of spam received on your website's protected forms. Each form can be set to one of the following options:"); $output .= '
'; $output .= t("Data is processsed and stored as explained in our Web Service Privacy Policy. It is your responsibility to provide any necessary notices and obtain the appropriate consent regarding Mollom's use of your data. For more information, see How Mollom Works and the Mollom FAQ.", array( '@mollom-privacy' => 'http://mollom.com/service-agreement-free-subscriptions', '@mollom-works' => 'http://mollom.com/how-mollom-works', '@mollom-faq' => 'http://mollom.com/faq') ); $output .= '
'; return $output; } } /** * Implements hook_link(). */ function mollom_link($type, $object, $teaser = FALSE) { $links = array(); // Only show the links if the module is configured. if (_mollom_status() === TRUE) { if ($type == 'comment' && user_access('administer comments') && mollom_get_mode('comment_form')) { $links['mollom_report'] = array( 'title' => t('report to Mollom'), 'href' => 'mollom/report/comment/' . $object->cid, 'query' => drupal_get_destination(), ); } elseif ($type == 'node' && user_access('administer nodes') && mollom_get_mode($object->type . '_node_form')) { $links['mollom_report'] = array( 'title' => t('report to Mollom'), 'href' => 'mollom/report/node/' . $object->nid, 'query' => drupal_get_destination(), ); } } return $links; } /** * Implements hook_menu(). */ function mollom_menu() { $items['mollom/report/%/%'] = array( 'title' => 'Report to Mollom', 'page callback' => 'drupal_get_form', 'page arguments' => array('mollom_report_form', 2, 3), 'access callback' => 'mollom_report_access', 'access arguments' => array(2, 3), 'file' => 'mollom.pages.inc', 'type' => MENU_CALLBACK, ); $items['admin/settings/mollom'] = array( 'title' => 'Mollom', 'description' => 'Mollom is a web service that helps you manage your community.', 'page callback' => 'mollom_admin_form_list', 'access arguments' => array('administer mollom'), 'file' => 'mollom.admin.inc', ); $items['admin/settings/mollom/forms'] = array( 'title' => 'Forms', 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10, ); $items['admin/settings/mollom/add'] = array( 'title' => 'Add form', 'page callback' => 'drupal_get_form', 'page arguments' => array('mollom_admin_configure_form'), 'access arguments' => array('administer mollom'), 'type' => MENU_LOCAL_TASK, 'file' => 'mollom.admin.inc', ); $items['admin/settings/mollom/manage/%mollom_form'] = array( 'title' => 'Configure', 'page callback' => 'drupal_get_form', 'page arguments' => array('mollom_admin_configure_form', 4), 'access arguments' => array('administer mollom'), 'file' => 'mollom.admin.inc', ); $items['admin/settings/mollom/unprotect/%mollom_form'] = array( 'title' => 'Unprotect form', 'page callback' => 'drupal_get_form', 'page arguments' => array('mollom_admin_unprotect_form', 4), 'access arguments' => array('administer mollom'), 'type' => MENU_CALLBACK, 'file' => 'mollom.admin.inc', ); $items['admin/settings/mollom/blacklist'] = array( 'title' => 'Blacklist', 'description' => 'Configure word and URL blacklists.', 'page callback' => 'mollom_admin_blacklist', 'access arguments' => array('administer mollom'), 'type' => MENU_LOCAL_TASK, 'file' => 'mollom.admin.inc', ); $items['admin/settings/mollom/blacklist/delete'] = array( 'title' => 'Delete', 'page callback' => 'drupal_get_form', 'page arguments' => array('mollom_admin_blacklist_delete', 5, 6), 'access arguments' => array('administer mollom'), 'type' => MENU_CALLBACK, 'file' => 'mollom.admin.inc', ); $items['admin/settings/mollom/settings'] = array( 'title' => 'Settings', 'description' => 'Configure Mollom keys and global settings.', 'page callback' => 'drupal_get_form', 'page arguments' => array('mollom_admin_settings'), 'access arguments' => array('administer mollom'), 'type' => MENU_LOCAL_TASK, 'file' => 'mollom.admin.inc', ); $items['admin/reports/mollom'] = array( 'title' => 'Mollom statistics', 'description' => 'Reports and usage statistics for the Mollom module.', 'page callback' => 'drupal_get_form', 'page arguments' => array('mollom_reports_page'), 'access callback' => '_mollom_access', 'access arguments' => array('administer mollom'), 'file' => 'mollom.admin.inc', ); // AJAX callback to request new CAPTCHA. $items['mollom/captcha/%/%'] = array( 'page callback' => 'mollom_captcha_js', 'page arguments' => array(2, 3), 'access callback' => '_mollom_access', 'file' => 'mollom.pages.inc', 'type' => MENU_CALLBACK, ); return $items; } /** * Access callback; check if the module is configured. * * This function does not actually check whether Mollom keys are valid for the * site, but just if the keys have been entered. * * @param $permission * An optional permission string to check with user_access(). * * @return * TRUE if the module has been configured and user_access() has been checked, * FALSE otherwise. */ function _mollom_access($permission = FALSE) { return variable_get('mollom_public_key', '') && variable_get('mollom_private_key', '') && (!$permission || user_access($permission)); } /** * Menu access callback; Determine access to report to Mollom. * * @param $entity * The entity type of the data to report. * @param $id * The entity id of the data to report. */ function mollom_report_access($entity, $id) { // The special entity 'session' means that $id is a Mollom session_id, which // can always be reported by everyone. if ($entity == 'session') { return !empty($id) ? TRUE : FALSE; } // Retrieve information about all protectable forms. We use the first valid // definition, because we assume that multiple form definitions just denote // variations of the same entity (e.g. node content types). foreach (mollom_get_form_info() as $form_id => $info) { if (!isset($info['entity']) || $info['entity'] != $entity) { continue; } // If there is a 'report access callback', invoke it. if (isset($info['report access callback']) && function_exists($info['report access callback'])) { $function = $info['report access callback']; return $function($entity, $id); } // Otherwise, if there is a 'report access' list of permissions, iterate // over them. if (isset($info['report access'])) { foreach ($info['report access'] as $permission) { if (user_access($permission)) { return TRUE; } } } } // If we end up here, then the current user is not permitted to report this // content. return FALSE; } /** * Implements hook_perm(). */ function mollom_perm() { return array( 'administer mollom', 'bypass mollom protection', ); } /** * Implements hook_flush_caches(). */ function mollom_flush_caches() { return array('cache_mollom'); } /** * Load a Mollom data record from the database. * * @param $entity * The entity type to retrieve data for. * @param $id * The entity id to retrieve data for. */ function mollom_data_load($entity, $id) { return db_fetch_object(db_query_range("SELECT * FROM {mollom} WHERE entity = '%s' AND did = '%s'", array($entity, $id), 0, 1)); } /** * Save Mollom validation data to the database. * * Based on the specified entity type and id, this function stores the * validation results returned by Mollom in the database. The stored data * is an associative array containing Mollom session information for the posted * content: * - session: The session ID returned by the Mollom server. * - quality: A quality rating assigned to the content to tell whether or not * it's spam. * - reputation: The reputation of the author. * - languages: An array containing language codes the content might be * written in. * * @param $entity * The entity type of the data to save. * @param $id * The entity id the data belongs to. * * @todo Remove usage of global $mollom_response variable. */ function mollom_data_save($entity, $id) { // Nothing to do, if we do not have a valid Mollom response. if (!isset($GLOBALS['mollom_response']['session_id'])) { return FALSE; } $data = $GLOBALS['mollom_response']; $data['session'] = $data['session_id']; $data['entity'] = $entity; $data['did'] = $id; // Convert languages into a string. if (!empty($data['languages'])) { $data['languages'] = implode(' ', $data['languages']); } // Merge in default values that may not exist in the response. $data += array( 'languages' => '', 'quality' => '', 'reputation' => '', ); $update = db_result(db_query_range("SELECT 'did' FROM {mollom} WHERE entity = '%s' AND did = '%s'", $entity, $id, 0, 1)); drupal_write_record('mollom', $data, $update ? $update : array()); return $data; } /** * Delete a Mollom data record from the database. * * @param $entity * The entity type to retrieve data for. * @param $id * The entity id to retrieve data for. */ function mollom_data_delete($entity, $id) { return db_query("DELETE FROM {mollom} WHERE entity = '%s' AND did = '%s'", array($entity, $id)); } /** * Implements hook_form_alter(). * * This function intercepts all forms in Drupal and Mollom-enables them if * necessary. */ function mollom_form_alter(&$form, &$form_state, $form_id) { // Site administrators don't have their content checked with Mollom. if (!user_access('bypass mollom protection') && _mollom_status() === TRUE) { // Retrieve configuration for this form. if ($mollom_form = mollom_form_load($form_id)) { // Determine whether to bypass validation for the current user. foreach ($mollom_form['bypass access'] as $permission) { if (user_access($permission)) { return; } } // Compute the weight of the CAPTCHA so we can position it in the form. $weight = 99999; foreach (element_children($form) as $key) { // Scan the top-level form elements for buttons. if (isset($form[$key]['#type']) && in_array($form[$key]['#type'], array('submit', 'button', 'image_button'))) { // For each button, slightly increase the weight to allocate room for // the CAPTCHA. if (isset($form[$key]['#weight'])) { $form[$key]['#weight'] = $form[$key]['#weight'] + 0.0002; } else { $form[$key]['#weight'] = 1.0002; } // We want to position the CAPTCHA just before the first button, so // we make the CAPTCHA's weight slightly lighter than the lightest // button's weight. $weight = min($weight, $form[$key]['#weight'] - 0.0001); } } // Add Mollom form widget. $form['mollom'] = array( '#type' => 'mollom', '#mollom_form' => $mollom_form, '#weight' => $weight, ); // Add Mollom form validation handlers. $form['#validate'][] = 'mollom_validate_analysis'; $form['#validate'][] = 'mollom_validate_captcha'; // Add a submit handler to remove form state storage. $form['#submit'][] = 'mollom_form_submit'; // Add link to privacy policy on forms protected via textual analysis, // if enabled. if ($mollom_form['mode'] == MOLLOM_MODE_ANALYSIS && variable_get('mollom_privacy_link', 1)) { $form['mollom']['privacy'] = array( '#prefix' => '@messageResult:
@result', array('@message' => print_r($data, TRUE), '@result' => print_r($result, TRUE))); break; case MOLLOM_ANALYSIS_SPAM: form_set_error('mollom', t('Your submission has triggered the spam filter and will not be accepted.')); watchdog('mollom', 'Spam:
@messageResult:
@result', array('@message' => print_r($data, TRUE), '@result' => print_r($result, TRUE))); break; default: // Fall back to a CAPTCHA. form_set_error('mollom', t("To complete this form, please complete the word verification below.")); watchdog('mollom', 'Unsure:
@messageResult:
@result', array('@message' => print_r($data, TRUE), '@result' => print_r($result, TRUE))); $form_state['mollom']['require_captcha'] = TRUE; break; } } /** * Form validation handler for CAPTCHA form element. */ function mollom_validate_captcha($form, &$form_state) { if (!$form_state['mollom']['require_captcha']) { return; } // When re-validating a form that already passed a CAPTCHA in a previous // request, we need to re-populate our global variable for mollom_data_save(). if ($form_state['mollom']['passed_captcha']) { $GLOBALS['mollom_response'] = $form_state['mollom']['response']; return; } // Bail out if no value was provided. if (empty($form_state['values']['mollom']['captcha'])) { form_set_error('mollom][captcha', t('The CAPTCHA field is required.')); return; } // Check the CAPTCHA result. $result = mollom('mollom.checkCaptcha', array( 'session_id' => $form_state['mollom']['session_id'], 'captcha_result' => $form_state['values']['mollom']['captcha'], 'author_ip' => ip_address(), )); // Store the response for #submit handlers. $form_state['mollom']['response']['session_id'] = $form_state['mollom']['session_id']; $form_state['mollom']['response']['spam'] = (int) $result; // @todo Only used for Contact module and mail integration in general. $GLOBALS['mollom_response'] = $form_state['mollom']['response']; // Explictly check for TRUE, since mollom.checkCaptcha() can also return an // error message (e.g. expired or invalid session_id). if ($result === TRUE) { $form_state['mollom']['passed_captcha'] = TRUE; watchdog('mollom', 'Correct CAPTCHA:
@data', array('@data' => print_r($form_state['values'], TRUE))); } else { form_set_error('mollom][captcha', t('The CAPTCHA was not completed correctly. Please complete this new CAPTCHA and try again.')); watchdog('mollom', 'Incorrect CAPTCHA:@data', array('@data' => print_r($form_state['values'], TRUE))); } } /** * Form element #pre_render callback for CAPTCHA element. * * Conditionally alters the #type of the CAPTCHA form element into a 'hidden' * element if the response was correct. If it was not, then we empty the value * of the textfield to allow the user to re-enter a new one. * * This #pre_render trick is required, because form API validation does not * allow form validation handlers to alter the actual form structure. Both the * form constructor function and the #process callback for the 'mollom' element * are therefore executed too early (before form validation), so the CAPTCHA * element still contains not yet validated (default) values. * We also cannot invoke a form validation handler during form construction or * processing, because mollom_form_get_values() would be invoked too early * and therefore $form_state['values'] would not contain any additions from * form validation functions like mollom_comment_form_validate(). * @see http://drupal.org/node/642702 */ function mollom_pre_render_mollom($element) { $form_state['mollom'] = &$element['#mollom']; // Request and inject a CAPTCHA when required; but also in case validation // through textual analysis failed. if ($form_state['mollom']['require_captcha'] && !$form_state['mollom']['passed_captcha']) { $element['captcha']['#required'] = TRUE; // Empty the CAPTCHA field value, since the user has to re-enter a new one. $element['captcha']['#value'] = ''; // Prevent the page cache from storing a form containing a CAPTCHA element. $GLOBALS['conf']['cache'] = CACHE_DISABLED; $data['author_ip'] = ip_address(); if (!empty($form_state['mollom']['session_id'])) { $data['session_id'] = $form_state['mollom']['session_id']; } if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') { $data['ssl'] = TRUE; } $result = mollom('mollom.getImageCaptcha', $data); // If we get a response, add the image CAPTCHA to the form element. if (isset($result['session_id']) && isset($result['url'])) { $captcha = ''; $captcha .= ''; // @todo This suffix needs to be injected via JavaScript. $captcha .= ' (' . t('play audio CAPTCHA') . ')'; $element['captcha']['#field_prefix'] = $captcha; // Assign the session ID returned by Mollom. $form_state['mollom']['session_id'] = $result['session_id']; $form_state['mollom']['response']['session_id'] = $result['session_id']; } // Otherwise, we have a communication or configuration error. // @todo Short-cut form processing entirely in this case; see also // mollom_validate_captcha(). else { $form_state['mollom']['require_analysis'] = FALSE; $form_state['mollom']['require_captcha'] = FALSE; return array(); } } // If no CAPTCHA is required or the response was correct, hide the CAPTCHA. elseif (!$form_state['mollom']['require_captcha'] || $form_state['mollom']['passed_captcha']) { $element['captcha']['#access'] = FALSE; } // If we received a Mollom session id via textual analysis or a CAPTCHA // request, inject it to the form. $timestamp = time(); if (!empty($form_state['mollom']['session_id'])) { $element['session_id']['#value'] = $timestamp . '-' . $form_state['mollom']['session_id']; // Store the Mollom session id in the user session to force a // session for anonymous users in Drupal 7 and Drupal 6 Pressflow. // @see mollom_exit() // @see mollom_form_submit() $_SESSION['mollom_sessions'][$form_state['mollom']['session_id']] = $timestamp; } if (!empty($form_state['mollom']['session_id'])) { cache_set($form_state['mollom']['session_id'], $form_state['mollom'], 'cache_mollom', $timestamp + 21600); } return $element; } /** * Form submit handler to flush Mollom session and form information from cache. */ function mollom_form_submit($form_id, &$form_state) { // Some modules are implementing multi-step forms without separate form // submit handlers. In case we reach here and the form will be rebuilt, we // need to defer our submit handling until final submission. if (!empty($form_state['rebuild'])) { return; } // If an 'entity' and a 'post_id' mapping was provided via // hook_mollom_form_info(), try to automatically store Mollom session data. if (!empty($form_state['mollom']['entity']) && isset($form_state['mollom']['mapping']['post_id'])) { // For new entities, the entity's form submit handler will have added the // new entity id value into $form_state['values'], so we need to rebuild the // data mapping. $data = mollom_form_get_values($form_state['values'], $form_state['mollom']['enabled_fields'], $form_state['mollom']['mapping']); // We only consider non-empty and non-zero values as valid entity ids. if (!empty($data['post_id'])) { mollom_data_save($form_state['mollom']['entity'], $data['post_id']); } } // Flush Mollom session information from database cache and user session. if (!empty($form_state['mollom']['session_id'])) { $session_id = $form_state['mollom']['session_id']; cache_clear_all($session_id, 'cache_mollom'); unset($_SESSION['mollom_sessions'][$session_id]); } // Remove Mollom session information from form state to account for unforeseen // new builds of the form. unset($form_state['mollom']); } /** * Implements hook_exit(). */ function mollom_exit() { // Expire all mollom session IDs as soon as possible. $now = time(); if (isset($_SESSION['mollom_sessions'])) { foreach ($_SESSION['mollom_sessions'] as $id => $timestamp) { if ($now - $timestamp > 30 * 60) { unset($_SESSION['mollom_sessions'][$id]); } } } // If all sessions were removed, also remove our storage key. if (isset($_SESSION) && empty($_SESSION['mollom_sessions'])) { unset($_SESSION['mollom_sessions']); } } /** * @} End of "defgroup mollom_form_api". */ /** * Call a remote procedure at the Mollom server. * * This function automatically adds the information required to authenticate * against Mollom. * * @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. */ function mollom($method, $data = array()) { module_load_include('inc', 'mollom'); $messages = array(); // Initialize refresh variable. $refresh = FALSE; // 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( 'text' => 'Refreshed servers: %servers', 'arguments' => array('%servers' => implode(', ', $servers)), ); // Store the list of servers in the database. variable_set('mollom_servers', $servers); } 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, $method, $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( 'text' => 'Refreshed servers: %servers', 'arguments' => array('%servers' => implode(', ', $servers)), ); } } elseif ($error->code === MOLLOM_REDIRECT) { // Try the next server in the list. $next = next($servers); $messages[] = array( 'text' => 'Server %server redirected to: %next.', 'arguments' => array('%server' => $server, '%next' => $next), ); } else { $messages[] = array( 'text' => 'Error @errno from %server: %message for method %method:@data', 'arguments' => array( '@errno' => $error->code, '%server' => $server, '%message' => $error->message, '%method' => $method, '@data' => print_r($data, TRUE), ), ); // Instantly return upon a 'real' error. if ($error->code === MOLLOM_ERROR) { _mollom_watchdog($messages, WATCHDOG_ERROR); return MOLLOM_ERROR; } // Otherwise, try the next server. next($servers); } } else { _mollom_watchdog($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( 'text' => 'All servers unreachable or returning errors. The server list was emptied.', ); _mollom_watchdog($messages, WATCHDOG_ERROR); return NETWORK_ERROR; } /** * Helper function for mollom() to invoke watchdog() with cumulative messages. * * We do not want false errors to clutter the log, for example, when the server * list failed, but we were able to retrieve new servers. We therefore collect * all messages and invoke this function in mollom() right before returning any * XML-RPC response with the entire stack of collected messages. * This is also required for tests to pass. */ function _mollom_watchdog($messages, $severity) { foreach ($messages as $message) { watchdog('mollom', $message['text'], !empty($message['arguments']) ? $message['arguments'] : NULL, $severity); } } /** * Send feedback to Mollom. */ function _mollom_send_feedback($session_id, $feedback = 'spam') { mollom('mollom.sendFeedback', array( 'session_id' => $session_id, 'feedback' => $feedback, )); watchdog('mollom', 'Reported session id %session as %feedback.', array( '%session' => $session_id, '%feedback' => $feedback, )); } /** * Fetch the site's Mollom statistics from the API. * * @param $refresh * A boolean if TRUE, will force the statistics to be re-fetched and stored * in the cache. * * @return * An array of statistics. */ function mollom_get_statistics($refresh = FALSE) { $statistics = FALSE; $cache = cache_get('mollom:statistics'); // Only fetch if $refresh is TRUE, the cache is empty, or the cache is expired. if ($refresh || !$cache || time() >= $cache->expire) { if (_mollom_status() === TRUE) { $statistics = drupal_map_assoc(array( 'total_days', 'total_accepted', 'total_rejected', 'yesterday_accepted', 'yesterday_rejected', 'today_accepted', 'today_rejected', )); foreach ($statistics as $statistic) { $result = mollom('mollom.getStatistics', array('type' => $statistic)); if ($result === NETWORK_ERROR || $result === MOLLOM_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 // connect to the Mollom service. $statistics = FALSE; break; } else { $statistics[$statistic] = $result; } } } // Cache the statistics and set them to expire in one hour. cache_set('mollom:statistics', $statistics, 'cache', time() + 3600); } else { $statistics = $cache->data; } return $statistics; } /** * Implements hook_content_extra_fields(). * * Allow users of CCK to re-order the CAPTCHA field on node forms through the * CCK UI. */ function mollom_content_extra_fields($type_name) { if ($mollom_form = mollom_form_load($type_name . '_node_form')) { $extras['mollom'] = array( 'label' => t('Mollom'), 'description' => t('Mollom CAPTCHA'), 'weight' => 99999, ); return $extras; } } /** * @name mollom_node Node module integration for Mollom. * @{ */ /** * Implements hook_mollom_form_info(). */ function node_mollom_form_info() { $types = node_get_types('types'); $forms = array(); foreach ($types as $type) { $form_id = $type->type . '_node_form'; $forms[$form_id] = array( 'title' => t('@name form', array('@name' => $type->name)), // @todo This is incompatible with node access. 'bypass access' => array('administer nodes', 'edit any ' . $type->type . ' content'), 'report access callback' => 'node_mollom_report_access', 'report delete callback' => 'node_mollom_report_delete', 'entity' => 'node', 'elements' => array(), 'mapping' => array( 'post_id' => 'nid', 'author_name' => 'name', ), ); // @see node_content_form() if ($type->has_title) { $forms[$form_id]['elements']['title'] = check_plain($type->title_label); $forms[$form_id]['mapping']['post_title'] = 'title'; } if ($type->has_body) { $forms[$form_id]['elements']['body'] = check_plain($type->body_label); } // Add CCK fields by default. if (module_exists('content')) { $content_info = content_types($type->type); foreach ($content_info['fields'] as $field_name => $field) { // We only consider text fields for text analysis. if ($field['type'] == 'text') { $forms[$form_id]['elements'][$field_name] = check_plain(t($field['widget']['label'])); } } } } return $forms; } /** * Implements hook_nodeapi(). */ function mollom_nodeapi($node, $op) { if ($op == 'insert') { mollom_data_save('node', $node->nid); } elseif ($op == 'delete') { mollom_data_delete('node', $node->nid); } } /** * Implements hook_form_FORMID_alter(). * * Hook into the mass comment administration page and add some operations to * communicate ham/spam to the XML-RPC server. * * @see mollom_node_admin_overview_submit() */ function mollom_form_node_admin_content_alter(&$form, $form_state) { module_load_include('inc', 'mollom', 'mollom.admin'); $form['admin']['options']['operation']['#options']['mollom-unpublish'] = t('Report to Mollom and unpublish'); $form['admin']['options']['operation']['#options']['mollom-delete'] = t('Report to Mollom and delete'); $form['#validate'][] = 'mollom_node_admin_overview_submit'; } /** * Mollom report access callback; Determine access to report and delete a node. */ function node_mollom_report_access($entity, $id) { $node = node_load($id); return $node && node_access('delete', $node); } /** * Mollom report delete callback; Deletes a node. */ function node_mollom_report_delete($entity, $id) { node_delete($id); } /** * @} End of "name mollom_node". */ /** * @name mollom_comment Comment module integration for Mollom. * @{ */ /** * Implements hook_mollom_form_info(). */ function comment_mollom_form_info() { $forms['comment_form'] = array( 'title' => t('Comment form'), 'mode' => MOLLOM_MODE_ANALYSIS, 'bypass access' => array('administer comments'), 'report access' => array('administer comments'), 'report delete callback' => 'comment_mollom_report_delete', 'entity' => 'comment', 'elements' => array( 'subject' => t('Subject'), 'comment' => t('Comment'), ), 'mapping' => array( 'post_id' => 'cid', 'post_title' => 'subject', // In D6, comment_form() dynamically uses different form elements for // anonymous users, authenticated users, and comment administrators. 'author_name' => 'name', 'author_mail' => 'mail', 'author_url' => 'homepage', ), ); return $forms; } /** * Implements hook_form_FORMID_alter(). * * When a registered user posts a comment or when a comment administrator edits * an existing comment, comment_form() does not define 'name' and 'mail' form * elements, so our form element mapping will fail. * * @see comment_mollom_form_info() * @see mollom_comment_form_validate() * * @todo Remove in D7. */ function mollom_form_comment_form_alter(&$form, &$form_state) { if (isset($form['author']) || isset($form['admin']['author'])) { $form['#validate'][] = 'mollom_comment_form_validate'; } } /** * Form validation handler for comment_form(). * * @todo Remove in D7. */ function mollom_comment_form_validate($form, &$form_state) { // If there were no validation errors, prepare submitted form values for // validation via Mollom. if (!form_get_errors()) { // Author is a registered user or comment is edited by administrator. if (isset($form_state['values']['author'])) { // Populate 'name' with value of 'author'. if (!isset($form_state['values']['name'])) { form_set_value(array('#parents' => array('name')), $form_state['values']['author'], $form_state); } // Populate 'mail' based on corresponding user account. if (!isset($form_state['values']['mail'])) { // This should already be validated by comment_validate(), but we still // double-check that we have a valid account before trying to access it. $account = user_load(array('name' => $form_state['values']['author'])); if ($account) { form_set_value(array('#parents' => array('mail')), $account->mail, $form_state); } } } } } /** * Implements hook_comment(). */ function mollom_comment($comment, $op) { if ($op == 'insert') { mollom_data_save('comment', $comment['cid']); } elseif ($op == 'delete') { mollom_data_delete('comment', $comment->cid); } } /** * Implements hook_form_FORMID_alter(). * * Hook into the mass comment administration page and add some operations to * communicate ham/spam to the XML-RPC server. * * @see mollom_comment_admin_overview_submit() */ function mollom_form_comment_admin_overview_alter(&$form, $form_state) { module_load_include('inc', 'mollom', 'mollom.admin'); $form['options']['operation']['#options']['mollom-unpublish'] = t('Report to Mollom and unpublish'); $form['options']['operation']['#options']['mollom-delete'] = t('Report to Mollom and delete'); $form['#submit'][] = 'mollom_comment_admin_overview_submit'; } /** * Mollom report delete callback; Deletes a comment and its replies. * * @see comment_confirm_delete_submit() */ function comment_mollom_report_delete($entity, $id) { module_load_include('inc', 'comment', 'comment.admin'); $comment = _comment_load($id); // Delete the comment and its replies. _comment_delete_thread($comment); _comment_update_node_statistics($comment->nid); // Clear the cache so an anonymous user sees that his comment was deleted. cache_clear_all(); drupal_set_message(t('The comment has been deleted.')); } /** * @} End of "name mollom_comment". */ /** * @name mollom_user User module integration for Mollom. * @{ */ /** * Implements hook_mollom_form_info(). */ function user_mollom_form_info() { $forms['user_register'] = array( 'title' => t('User registration form'), 'mode' => MOLLOM_MODE_CAPTCHA, 'bypass access' => array('administer users'), 'entity' => 'user', 'mapping' => array( 'post_id' => 'uid', 'author_name' => 'name', 'author_mail' => 'mail', ), ); $forms['user_pass'] = array( 'title' => t('User password request form'), 'mode' => MOLLOM_MODE_CAPTCHA, 'bypass access' => array('administer users'), 'entity' => 'user', 'mapping' => array( 'post_id' => 'uid', 'author_name' => 'name', 'author_mail' => 'name', ), ); return $forms; } /** * @} End of "name mollom_user". */ /** * @name mollom_contact Contact module integration for Mollom. * @{ */ /** * Implements hook_mollom_form_info(). */ function contact_mollom_form_info() { $forms['contact_mail_page'] = array( 'title' => t('Site-wide contact form'), 'mode' => MOLLOM_MODE_ANALYSIS, 'bypass access' => array('administer site-wide contact form'), 'elements' => array( 'subject' => t('Subject'), 'message' => t('Message'), ), 'mapping' => array( 'post_title' => 'subject', 'author_name' => 'name', 'author_mail' => 'mail', ), ); $forms['contact_mail_user'] = array( 'title' => t('User contact form'), 'mode' => MOLLOM_MODE_ANALYSIS, 'bypass access' => array('administer users'), 'elements' => array( 'subject' => t('Subject'), 'message' => t('Message'), ), 'mapping' => array( 'post_title' => 'subject', 'author_name' => 'name', 'author_mail' => 'mail', ), ); return $forms; } /** * Implements hook_mail_alter(). * * Adds a "report as inappropriate" link to e-mails sent by Contact module. */ function mollom_mail_alter(&$message) { // Only attach the Mollom report link to mails sent by actual users and not // any mails sent by Drupal since they should never be reported as spam. $valid_ids = array('contact_page_mail', 'contact_page_copy', 'contact_user_mail', 'contact_user_copy'); if (isset($GLOBALS['mollom_response']['session_id']) && in_array($message['id'], $valid_ids)) { mollom_data_save('contact', $message['id']); $report_link = t('Report as inappropriate: @link', array( '@link' => url('mollom/report/session/' . $GLOBALS['mollom_response']['session_id'], array('absolute' => TRUE)), )); // Until D7, hook_mail_alter() accepts both arrays and strings. if (is_array($message['body'])) { $message['body'][] = $report_link; } else { $message['body'] .= "\n\n" . $report_link; } } } /** * @} End of "name mollom_contact". */