/** * The content callback for the Blacklist list of current entries. * * @param string $type * A particular list type to show (based on the entry 'reason'). */ function content($type = NULL) { MollomUtilities::getAdminAPIKeyStatus(); MollomUtilities::displayMollomTestModeWarning(); $items = BlacklistStorage::getList($type); $rows = array(); // Edit/delete. $header = array(); if (empty($type)) { $header['type'] = $this->t('List'); } $header['context'] = $this->t('Context'); $header['matches'] = $this->t('Matches'); $header['value'] = $this->t('Value'); $header['operations'] = $this->t('Operations'); foreach ($items as $entry) { $data = array($entry['context'], $entry['match'], $entry['value'], array('data' => array('#type' => 'operations', '#links' => array(array('title' => $this->t('Delete'), 'url' => Url::fromRoute('mollom.blacklist.delete', array('entry_id' => $entry['id']))))))); if (empty($type)) { array_unshift($data, $entry['reason']); } $rows[] = $data; } $build['table'] = array('#type' => 'table', '#header' => $header, '#rows' => $rows, '#empty' => $this->t('There are no entries in the blacklist.'), '#attributes' => array('id' => 'mollom-blacklist-list')); return $build; }
public function content() { MollomUtilities::getAdminAPIKeyStatus(); $config = $this->config('mollom.settings'); $embed_attributes = array('src' => 'https://mollom.com/statistics.swf?key=' . urlencode($config->get('keys.public')), 'quality' => 'high', 'width' => '100%', 'height' => '430', 'name' => 'Mollom', 'align' => 'middle', 'play' => 'true', 'loop' => 'false', 'allowScriptAccess' => 'sameDomain', 'type' => 'application/x-shockwave-flash', 'pluginspage' => 'http://www.adobe.com/go/getflashplayer', 'wmode' => 'transparent'); return array('#type' => 'markup', '#markup' => '<embed' . new Attribute($embed_attributes) . '></embed>', '#allowed_tags' => ['embed']); }
/** * {@inheritdoc} */ public function buildHeader() { MollomUtilities::getAdminAPIKeyStatus(); MollomUtilities::displayMollomTestModeWarning(); $header['label'] = $this->t('Form'); $header['protection_mode'] = $this->t('Protection mode'); return $header + parent::buildHeader(); }
/** * Overrides Drupal\Core\Form\FormInterface::buildForm(). */ public function buildForm(array $form, FormStateInterface $form_state, $entry_id = NULL) { MollomUtilities::getAdminAPIKeyStatus(); MollomUtilities::displayMollomTestModeWarning(); $entry = $this->setEntryById($entry_id)->getEntry(); $form['entry_id'] = array('#type' => 'value', '#value' => $entry_id); $form['reason'] = array('#type' => 'select', '#title' => $this->t('Type'), '#default_value' => $entry['reason'], '#options' => $this->getBlacklistTypeOptions(), '#required' => TRUE); $form['context'] = array('#type' => 'select', '#title' => $this->t('Context'), '#default_value' => $entry['context'], '#options' => $this->getContextOptions(), '#required' => TRUE); $form['match'] = array('#type' => 'select', '#title' => $this->t('Matches'), '#default_value' => $entry['match'], '#options' => $this->getMatchesOptions(), '#required' => TRUE); $form['value'] = array('#type' => 'textfield', '#title' => $this->t('Value'), '#default_value' => $entry['value'], '#required' => TRUE); $form['actions'] = array('#type' => 'actions'); $form['actions']['submit'] = array('#type' => 'submit', '#value' => $this->t('Save entry')); return $form; }
/** * Implements hook_init(). */ function onRequest() { // On all Mollom administration pages, check the module configuration and // display the corresponding requirements error, if invalid. $url = Url::fromRoute('<current>'); $current_path = $url->toString(); if (empty($_POST) && strpos($current_path, 'admin/config/content/mollom') === 0 && \Drupal::currentUser()->hasPermission('administer mollom')) { // Re-check the status on the settings form only. $status = \Drupal\mollom\Utility\MollomUtilities::getAPIKeyStatus($current_path == 'admin/config/content/mollom/settings'); if ($status !== TRUE) { // Fetch and display requirements error message, without re-checking. module_load_install('mollom'); $requirements = mollom_requirements('runtime', FALSE); if (isset($requirements['mollom']['description'])) { drupal_set_message($requirements['mollom']['description'], 'error'); } } } }
/** * 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; } } }
/** * Calls _mollom_status() directly to verify that current API keys are valid. */ protected function assertValidKeys() { $status = MollomUtilities::getAPIKeyStatus(TRUE); $this->assertMollomWatchdogMessages(); $this->assertIdentical($status['isVerified'], TRUE, t('Mollom servers can be contacted and testing API keys are valid.')); }
/** * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { $values = $form_state->getValues(); $config = $this->config('mollom.settings'); $config->set('keys.public', $values['keys']['public'])->set('keys.private', $values['keys']['private'])->set('fallback', $values['fallback'])->set('languages_expected', $values['languages_expected'])->set('privacy_link', $values['privacy_link'])->set('test_mode.enabled', $values['testing_mode'])->set('log_level', $values['log_level'])->set('captcha.audio.enabled', $values['audio_captcha_enabled'])->set('connection_timeout_seconds', $values['connection_timeout_seconds'])->save(); parent::submitForm($form, $form_state); // Update Mollom site record with local configuration. MollomUtilities::getAPIKeyStatus(TRUE, TRUE); }
/** * Gets the status of Mollom's API key configuration and also displays a * warning message if the Mollom API keys are not configured. * * To be used within the Mollom administration pages only. * * @param bool $force * (optional) Boolean whether to ignore the cached state and re-check. * Defaults to FALSE. * @param bool $update * (optional) Whether to update Mollom with locally stored configuration. * Defaults to FALSE. * * @return array * An associative array describing the current status of the module: * - isConfigured: Boolean whether Mollom API keys have been configured. * - isVerified: Boolean whether Mollom API keys have been verified. * - response: The response error code of the API verification request. * - ...: The full site resource, as returned by the Mollom API. * * @see Mollom::getAPIKeyStatus(). */ public static function getAdminAPIKeyStatus($force = FALSE, $update = FALSE) { $status = MollomUtilities::getAPIKeyStatus($force, $update); if (empty($_POST) && !$status['isVerified']) { // Fetch and display requirements error message, without re-checking. module_load_install('mollom'); $requirements = mollom_requirements('runtime', FALSE); if (isset($requirements['mollom']['description'])) { drupal_set_message($requirements['mollom']['description'], 'error'); } } return $status; }
/** * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state) { // Get anything we need form the base class. $form = parent::buildForm($form, $form_state); // Display any API key errors. MollomUtilities::getAdminAPIKeyStatus(); MollomUtilities::displayMollomTestModeWarning(); /* @var $entity \Drupal\mollom\Entity\FormInterface */ $entity = $this->getEntity(); $form_id = ''; if ($entity->isNew()) { // Determine if the form id selection just changed. $input = $form_state->getUserInput(); if (!empty($input['id'])) { $form_id = $input['id']; $mollom_form = $entity->initialize($form_id); } else { if ($query_form_id = \Drupal::request()->query->get('form_id', '')) { $form_id = $query_form_id; $mollom_form = $entity->initialize($form_id); } } } else { $form_id = $entity->id(); $mollom_form = $entity->initialize(); } $enabled_fields = []; if ($entity->isNew() && !empty($input['id'])) { foreach ($mollom_form['enabled_fields'] as $value) { $enabled_fields[] = rawurlencode($value); } // Set defaults back. // See https://www.drupal.org/node/1100170 $input['checks'] = $entity->getChecks(); $input['enabled_fields'] = $enabled_fields; $form_state->setUserInput($input); } else { foreach ($entity->getEnabledFields() as $value) { $enabled_fields[] = rawurldecode($value); } } // Build the form. if ($entity->isNew()) { $options = $this->getProtectableFormOptions(); if (empty($options)) { return $this->redirect('entity.mollom_form.list'); } $form['#attributes']['id'] = $this->getFormId(); $form['id'] = array('#type' => 'select', '#title' => $this->t('Mollom Form'), '#maxlength' => 255, '#options' => $options, '#default_value' => $form_id, '#empty_option' => t('Select a form to configure...'), '#required' => TRUE, '#ajax' => array('callback' => array($this, 'ajaxFormHandler'), 'wrapper' => $this->getFormId())); // Must select the form to protect prior to continuing. if (empty($form_id)) { return $form; } } else { $form['label'] = array('#title' => t('Protected form'), '#type' => 'textfield', '#default_value' => $entity->label(), '#disabled' => TRUE); } // Protection mode $modes = array(FormInterface::MOLLOM_MODE_ANALYSIS => $this->t('@option <em>(@recommended)</em>', array('@option' => $this->t('Text analysis'), '@recommended' => $this->t('recommended'))), FormInterface::MOLLOM_MODE_CAPTCHA => t('CAPTCHA only')); $form['mode'] = array('#type' => 'radios', '#title' => t('Protection mode'), '#options' => $modes, '#default_value' => isset($entity->mode) ? $entity->mode : key($modes)); $form['mode'][FormInterface::MOLLOM_MODE_ANALYSIS] = array('#description' => t('Mollom will analyze the post and will only show a CAPTCHA when it is unsure.')); $form['mode'][FormInterface::MOLLOM_MODE_CAPTCHA] = array('#description' => t('A CAPTCHA will be shown for every post. Only choose this if there are too few text fields to analyze.')); $form['mode'][FormInterface::MOLLOM_MODE_CAPTCHA]['#description'] .= '<br />' . t('Note: Page caching is disabled on all pages containing a CAPTCHA-only protected form.'); $all_permissions = $this->permissionHandler->getPermissions(); // Prepend Mollom's global permission to the list. if (empty($mollom_form['bypass access']) || !is_array($mollom_form['bypass access'])) { $mollom_form['bypass access'] = []; } array_unshift($mollom_form['bypass access'], 'bypass mollom protection'); $permissions = array(); if (isset($mollom_form['bypass access'])) { foreach ($mollom_form['bypass access'] as $permission) { $permissions[Html::getClass($permission)] = array('title' => $all_permissions[$permission]['title'], 'url' => Url::fromRoute('user.admin_permissions'), 'fragment' => 'module-' . $all_permissions[$permission]['provider']); } } $form['mode']['#description'] = t('The protection is omitted for users having any of the permissions: @permission-list', array('@permission-list' => \Drupal::theme()->render('links', array('links' => $permissions)))); // Textual analysis filters. $form['checks'] = array('#type' => 'checkboxes', '#title' => t('Text analysis checks'), '#options' => array('spam' => t('Spam'), 'profanity' => t('Profanity')), '#default_value' => $entity->getChecks(), '#states' => array('visible' => array('[name="mode"]' => array('value' => (string) FormInterface::MOLLOM_MODE_ANALYSIS)))); // Profanity check requires text to analyze; unlike the spam check, there // is no fallback in case there is no text. $form['checks']['profanity']['#access'] = !empty($mollom_form['elements']); // Form elements defined by hook_mollom_form_info() use the // 'parent][child' syntax, which Form API also uses internally for // form_set_error(), and which allows us to recurse into nested fields // during processing of submitted form values. However, since we are using // those keys also as internal values to configure the fields to use for // textual analysis, we need to encode them. Otherwise, a nested field key // would result in the following checkbox attribute: // '#name' => 'mollom[enabled_fields][parent][child]' // This would lead to a form validation error, because it is a valid key. // By encoding them, we prevent this from happening: // '#name' => 'mollom[enabled_fields][parent%5D%5Bchild]' $elements = array(); if (isset($mollom_form['elements']) && is_array($mollom_form['elements'])) { foreach ($mollom_form['elements'] as $key => $value) { $elements[rawurlencode($key)] = $value; } } $enabled_field_selections = []; foreach ($enabled_fields as $key => $value) { $enabled_field_selections[rawurlencode($key)] = rawurlencode($value); } $form['enabled_fields'] = array('#type' => 'checkboxes', '#title' => t('Text fields to analyze'), '#options' => $elements, '#default_value' => $enabled_field_selections, '#description' => t('Only enable fields that accept text (not numbers). Omit fields that contain sensitive data (e.g., credit card numbers) or computed/auto-generated values, as well as author information fields (e.g., name, e-mail).'), '#access' => !empty($mollom_form['elements']), '#states' => array('visible' => array('[name="mode"]' => array('value' => (string) FormInterface::MOLLOM_MODE_ANALYSIS)))); $form['mapping'] = array('#type' => 'value', '#value' => $mollom_form['mapping']); if ($entity->isNew()) { $form['module'] = array('#type' => 'value', '#value' => $mollom_form['module']); $form['label'] = array('#type' => 'value', '#value' => $mollom_form['title']); $form['entity'] = array('#type' => 'value', '#value' => $mollom_form['entity']); $form['bundle'] = array('#type' => 'value', '#value' => $mollom_form['bundle']); } $form['strictness'] = array('#type' => 'radios', '#title' => t('Text analysis strictness'), '#options' => array('normal' => t('@option <em>(@recommended)</em>', array('@option' => t('Normal'), '@recommended' => $this->t('recommended'))), 'strict' => t('Strict: Posts are more likely classified as spam'), 'relaxed' => t('Relaxed: Posts are more likely classified as ham')), '#default_value' => $entity->getStrictness(), '#states' => array('visible' => array('[name="mode"]' => array('value' => (string) FormInterface::MOLLOM_MODE_ANALYSIS)))); $form['unsure'] = array('#type' => 'radios', '#title' => t('When text analysis is unsure'), '#default_value' => $entity->getUnsure(), '#options' => array('captcha' => t('@option <em>(@recommended)</em>', array('@option' => t('Show a CAPTCHA'), '@recommended' => $this->t('recommended'))), 'moderate' => t('Retain the post for manual moderation'), 'binary' => t('Accept the post')), '#required' => $entity->getProtectionMode() == FormInterface::MOLLOM_MODE_ANALYSIS, '#states' => array('visible' => array('[name="mode"]' => array('value' => (string) FormInterface::MOLLOM_MODE_ANALYSIS), '[name="checks[spam]"]' => array('checked' => TRUE)))); // Only possible for forms supporting moderation of unpublished posts. $form['unsure']['moderate']['#access'] = !empty($mollom_form['moderation callback']); $form['discard'] = array('#type' => 'radios', '#title' => t('When text analysis identifies spam'), '#default_value' => $entity->getDiscard(), '#options' => array(1 => t('@option <em>(@recommended)</em>', array('@option' => t('Discard the post'), '@recommended' => $this->t('recommended'))), 0 => t('Retain the post for manual moderation')), '#required' => $entity->getProtectionMode() == FormInterface::MOLLOM_MODE_ANALYSIS, '#states' => array('visible' => array('[name="mode"]' => array('value' => (string) FormInterface::MOLLOM_MODE_ANALYSIS), '[name="checks[spam]"]' => array('checked' => TRUE)))); // Return the form. return $form; }