/** * Get the HTML markup for a Mollom CAPTCHA and add it to the Mollom element. * * @param array $element * The Mollom custom form element passed by reference. */ public static function addMollomCaptcha(&$element) { // Load the CAPTCHA from the Mollom API. $data = array('type' => in_array($element['captcha_required']['#value'], array('image', 'audio')) ? $element['captcha_required']['#value'] : 'image', 'ssl' => (int) (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on')); // If the requested type is audio, make sure it is enabled for the site. if ($data['type'] == 'audio') { if (!\Drupal::config('mollom.settings')->get('captcha.audio.enabled')) { $data['type'] = 'image'; } } if (!empty($element['contentId']['#value'])) { $data['contentId'] = $element['contentId']['#value']; } /** @var \Drupal\mollom\API\DrupalClient $mollom */ $mollom_service = \Drupal::service('mollom.client'); $captcha_result = $mollom_service->createCaptcha($data); // Add a log message to prevent the request log from appearing without a // message on CAPTCHA-only protected forms. \Drupal::logger('mollom')->notice('Retrieved new CAPTCHA: @id', array('@id' => isset($captcha_result['id']) ? $captcha_result['id'] : 'error')); // Check the response is valid. if (is_array($captcha_result) && isset($captcha_result['url'])) { $element['response']['captcha']['#value'] = $captcha_result; } else { $element['captcha']['#access'] = FALSE; \Drupal\mollom\Utility\MollomUtilities::handleFallback(); $element['captchaId']['#value'] = 'invalid'; return FALSE; } // Theme CAPTCHA output. // $element['captcha']['#theme'] = 'mollom_captcha'; $element['captcha']['captcha_display'] = array('#theme' => $data['type'] == 'audio' ? 'mollom_captcha_audio' : 'mollom_captcha_image', '#captcha_url' => $captcha_result['url'], '#weight' => 20); // Add the CAPTCHA and its data to the element. $element['captcha']['#access'] = TRUE; //$element['captcha']['#field_prefix'] = $captcha_rendered; $element['captcha']['captcha_input']['#attributes'] = array('title' => t('Enter the characters from the verification above.')); // The mollom.swfobject library is only added if swfobject is available on the site. // @see mollom_library_info_build(). if (\Drupal::service('library.discovery')->getLibraryByName('mollom', 'mollom.swfobject')) { $element['captcha']['#attached']['library'][] = 'mollom/mollom.swfobject'; } // Ensure that the latest CAPTCHA ID is output as value. $element['captchaId']['#value'] = $captcha_result['id']; // The form element cannot be marked as #required, since _form_validate() // would throw an element validation error on an empty value otherwise, // before the form-level validation handler is executed. // #access cannot default to FALSE, since the $form may be cached, and // Form API ignores user input for all elements that are not accessible. $element['captcha']['captcha_input']['#required'] = TRUE; return !empty($captcha_result['url']); }
/** * Form validation handler to perform textual analysis on submitted form values. */ public static function validateAnalysis(&$form, FormState $form_state) { if (!static::shouldValidate($form, $form_state)) { return; } /** @var \Drupal\mollom\Entity\Form $mollom_form */ $mollom = $form_state->getValue('mollom'); if (!$mollom['require_analysis']) { return FALSE; } // Perform textual analysis. $all_data = self::extractMollomValues($form_state->cleanValues(), $mollom['enabled_fields'], $mollom['mapping']); // Cancel processing upon invalid UTF-8 data. if ($all_data === FALSE) { return FALSE; } $data = $all_data; // Remove postId property; only used by submitForm(). if (isset($data['postId'])) { unset($data['postId']); } $contentId = isset($mollom['contentId']) ? $mollom['contentId'] : NULL; if (!empty($contentId)) { $data['id'] = $contentId; } if (is_array($mollom['checks'])) { $data['checks'] = $mollom['checks']; } $data['strictness'] = $mollom['strictness']; if (isset($mollom['type'])) { $data['type'] = $mollom['type']; } if (in_array('spam', $data['checks']) && $mollom['unsure'] == 'binary') { $data['unsure'] = 0; } // Allow modules to alter data sent. \Drupal::moduleHandler()->alter('mollom_content', $data); /** @var \Drupal\mollom\API\DrupalClient $mollom */ $mollom_service = \Drupal::service('mollom.client'); $result = $mollom_service->checkContent($data); // Use all available data properties for log messages below. $data += $all_data; // Trigger global fallback behavior if there is a unexpected result. if (!is_array($result) || !isset($result['id'])) { return MollomUtilities::handleFallback(); } // Set form values accordingly. Do not overwrite the entity ID. // @todo Rename 'id' to 'entity_id'. $result['contentId'] = $result['id']; unset($result['id']); // Store the response returned by Mollom. $form_state->setValue(array('mollom', 'response', 'content'), $result); $form_state->setValue('mollom', array_merge($mollom, $result)); // Ensure the latest content ID is output as value. // form_set_value() is effectless, as this is not a element-level but a // form-level validation handler. $form['mollom']['contentId']['#value'] = $result['contentId']; // Prepare watchdog message teaser text. $teaser = '--'; if (isset($data['postTitle'])) { $teaser = Unicode::truncate(strip_tags($data['postTitle']), 40); } elseif (isset($data['postBody'])) { $teaser = Unicode::truncate(strip_tags($data['postBody']), 40); } // Handle the profanity check result. if (isset($result['profanityScore']) && $result['profanityScore'] >= 0.5) { if ($mollom['discard']) { $form_state->setError($form, t('Your submission has triggered the profanity filter and will not be accepted until the inappropriate language is removed.')); } else { $form_state->setValue(['mollom', 'require_moderation'], TRUE); } Logger::addMessage(array('message' => 'Profanity: %teaser', 'arguments' => array('%teaser' => $teaser))); } // Handle the spam check result. // The Mollom API takes over state tracking for each content ID/session. The // spamClassification will usually turn into 'ham' after solving a CAPTCHA. // It may also change to 'spam', if the user replaced the values with very // spammy content. In any case, we always do what we are told to do. $form_state->setValue(['mollom', 'require_captcha'], FALSE); $form['mollom']['captcha']['#access'] = FALSE; if (isset($result['spamClassification'])) { switch ($result['spamClassification']) { case 'ham': $message = SafeMarkup::format('Ham: %teaser', array('%teaser' => $teaser)); \Drupal::logger('mollom')->notice($message); break; case 'spam': if ($mollom['discard']) { $form_state->setError($form, t('Your submission has triggered the spam filter and will not be accepted. @fp_message', array('@fp_message' => MollomUtilities::formatFalsePositiveMessage($form_state, $data)))); } else { $form_state->setValue(array('mollom', 'require_moderation'), TRUE); } $message = SafeMarkup::format('Spam: %teaser', array('%teaser' => $teaser)); \Drupal::logger('mollom')->notice($message); break; case 'unsure': if ($mollom['unsure'] == 'moderate') { $form_state->setValue(array('mollom', 'require_moderation'), TRUE); } else { $form_state->setValue(['mollom', 'captcha_response_id'], NULL); $form['mollom']['captcha_response_id']['#value'] = NULL; $form_state->setValue(array('mollom', 'require_captcha'), TRUE); // Require a new CAPTCHA and throw an error. $had_captcha = $form_state->get('mollom_had_captcha'); $form_state->setCached(FALSE); // Set the CAPTCHA type required indicator. $form_state->setValue(array('mollom', 'captcha_required'), $mollom['captcha_type']); $form['mollom']['captcha_required']['#value'] = $mollom['captcha_type']; $form['mollom']['captcha']['#access'] = TRUE; if (!empty($had_captcha)) { $form_state->setErrorByName('mollom][captcha', t('The word verification was not completed correctly. Please complete this new word verification and try again. @fp_message', array('@fp_message' => MollomUtilities::formatFalsePositiveMessage($form_state, $data)))); } else { $form_state->setErrorByName('mollom][captcha', t('To complete this form, please complete the word verification.')); } } $message = SafeMarkup::format('Unsure: %teaser', array('%teaser' => $teaser)); \Drupal::logger('mollom')->notice($message); break; case 'unknown': default: // If we end up here, Mollom responded with a unknown spamClassification. // Normally, this should not happen, but if it does, log it. As there // could be multiple reasons for this, it is not safe to trigger the // fallback mode. $message = SafeMarkup::format('Unknown: %teaser', array('%teaser' => $teaser)); \Drupal::logger('mollom')->notice($message); break; } } }