/**
  * @covers ::handleFormErrors
  * @covers ::setElementErrorsFromFormState
  */
 public function testSetElementErrorsFromFormState()
 {
     $form_error_handler = $this->getMockBuilder('Drupal\\Core\\Form\\FormErrorHandler')->setMethods(['drupalSetMessage'])->getMock();
     $form = ['#parents' => []];
     $form['test'] = ['#type' => 'textfield', '#title' => 'Test', '#parents' => ['test'], '#id' => 'edit-test'];
     $form_state = new FormState();
     $form_state->setErrorByName('test', 'invalid');
     $form_error_handler->handleFormErrors($form, $form_state);
     $this->assertSame('invalid', $form['test']['#errors']);
 }
 /**
  * @covers ::handleFormErrors
  * @covers ::setElementErrorsFromFormState
  */
 public function testSetElementErrorsFromFormState()
 {
     $form_error_handler = $this->getMockBuilder('Drupal\\Core\\Form\\FormErrorHandler')->setConstructorArgs([$this->getStringTranslationStub(), $this->getMock('Drupal\\Core\\Utility\\LinkGeneratorInterface')])->setMethods(['drupalSetMessage'])->getMock();
     $form = ['#parents' => []];
     $form['test'] = ['#type' => 'textfield', '#title' => 'Test', '#parents' => ['test'], '#id' => 'edit-test'];
     $form_state = new FormState();
     $form_state->setErrorByName('test', 'invalid');
     $form_error_handler->handleFormErrors($form, $form_state);
     $this->assertSame('invalid', $form['test']['#errors']);
 }
Exemple #3
0
 /**
  * Given an array of values and an array of fields, extract data for use.
  *
  * This function generates the data to send for validation to Mollom by walking
  * through the submitted form values and
  * - copying element values as specified via 'mapping' in hook_mollom_form_info()
  *   into the dedicated data properties
  * - collecting and concatenating all fields that have been selected for textual
  *   analysis into the 'post_body' property
  *
  * The processing accounts for the following possibilities:
  * - A field was selected for textual analysis, but there is no submitted form
  *   value. The value should have been appended to the 'post_body' property, but
  *   will be skipped.
  * - A field is contained in the 'mapping' and there is a submitted form value.
  *   The value will not be appended to the 'post_body', but instead be assigned
  *   to the specified data property.
  * - All fields specified in 'mapping', for which there is a submitted value,
  *   but which were NOT selected for textual analysis, are assigned to the
  *   specified data property. This is usually the case for form elements that
  *   hold system user information.
  *
  * @param $form_state
  *   An associative array containing
  *   - values: The submitted form values.
  *   - buttons: A list of button form elements. See form_state_values_clean().
  * @param $fields
  *   A list of strings representing form elements to extract. Nested fields are
  *   in the form of 'parent][child'.
  * @param $mapping
  *   An associative array of form elements to map to Mollom's dedicated data
  *   properties. See hook_mollom_form_info() for details.
  *
  * @see hook_mollom_form_info()
  */
 public static function extractMollomValues(FormState $form_state, $fields, $mapping)
 {
     $user = \Drupal::currentUser();
     // All elements specified in $mapping must be excluded from $fields, as they
     // are used for dedicated $data properties instead. To reduce the parsing code
     // size, we are turning a given $mapping of e.g.
     //   array('post_title' => 'title_form_element')
     // into
     //   array('title_form_element' => 'post_title')
     // and we reset $mapping afterwards.
     // When iterating over the $fields, this allows us to quickly test whether the
     // current field should be excluded, and if it should, we directly get the
     // mapped property name to rebuild $mapping with the field values.
     $exclude_fields = array();
     if (!empty($mapping)) {
         $exclude_fields = array_flip($mapping);
     }
     $mapping = array();
     // Process all fields that have been selected for text analysis.
     $post_body = array();
     foreach ($fields as $field) {
         // Nested elements use a key of 'parent][child', so we need to recurse.
         $parents = explode('][', $field);
         $value = $form_state->getValue($parents);
         // If this field was contained in $mapping and should be excluded, add it to
         // $mapping with the actual form element value, and continue to the next
         // field. Also unset this field from $exclude_fields, so we can process the
         // remaining mappings below.
         if (isset($exclude_fields[$field])) {
             if (is_array($value)) {
                 $value = implode(' ', MollomUtilities::flattenFormValue($value));
             }
             $mapping[$exclude_fields[$field]] = $value;
             unset($exclude_fields[$field]);
             continue;
         }
         // Only add form element values that are not empty.
         if (isset($value)) {
             // UTF-8 validation happens later.
             if (is_string($value) && strlen($value)) {
                 $post_body[$field] = $value;
             } elseif (is_array($value) && !empty($value)) {
                 // Ensure we have a flat, indexed array to implode(); form values of
                 // field_attach_form() use several subkeys.
                 $value = MollomUtilities::flattenFormValue($value);
                 $post_body = array_merge($post_body, $value);
             }
         }
     }
     $post_body = implode("\n", $post_body);
     // Try to assign any further form values by processing the remaining mappings,
     // which have been turned into $exclude_fields above. All fields that were
     // already used for 'post_body' no longer exist in $exclude_fields.
     foreach ($exclude_fields as $field => $property) {
         // If the postTitle field was not included in the enabled fields, then don't
         // set it's mapping here.
         if ($property === 'post_title' && !in_array($field, $fields)) {
             continue;
         }
         // Nested elements use a key of 'parent][child', so we need to recurse.
         $parents = explode('][', $field);
         $value = $form_state->getValue($parents);
         if (isset($value)) {
             if (is_array($value)) {
                 $value = MollomUtilities::flattenFormValue($value);
                 $value = implode(' ', $value);
             }
             $mapping[$property] = $value;
         }
     }
     // Build the data structure expected by the Mollom API.
     $data = array();
     // Post id; not sent to Mollom.
     // @see submitForm()
     if (!empty($mapping['post_id'])) {
         $data['postId'] = $mapping['post_id'];
     }
     // Post title.
     if (!empty($mapping['post_title'])) {
         $data['postTitle'] = $mapping['post_title'];
     }
     // Post body.
     if (!empty($post_body)) {
         $data['postBody'] = $post_body;
     }
     // Author ID.
     // If a non-anonymous user ID was mapped via form values, use that.
     if (!empty($mapping['author_id'])) {
         $data['authorId'] = $mapping['author_id'];
     } elseif (!empty($user->id())) {
         $data['authorId'] = $user->id();
     }
     // Load the user account of the author, if any, for the following author*
     // property assignments.
     $account = FALSE;
     if (isset($data['authorId'])) {
         /** @var \Drupal\user\Entity\User $account */
         $account = User::load($data['authorId']);
         $author_username = $account->getUsername();
         $author_email = $account->getEmail();
         // Author creation date.
         $data['authorCreated'] = $account->getCreatedTime();
         // In case a post of a registered user is edited and a form value mapping
         // exists for author_id, but no form value mapping exists for author_name,
         // use the name of the user account associated with author_id.
         // $account may be the same as the currently logged-in $user at this point.
         if (!empty($author_username)) {
             $data['authorName'] = $author_username;
         }
         if (!empty($author_email)) {
             $data['authorMail'] = $author_email;
         }
     }
     // Author name.
     // A form value mapping always has precedence.
     if (!empty($mapping['author_name'])) {
         $data['authorName'] = $mapping['author_name'];
     }
     // Author e-mail.
     if (!empty($mapping['author_mail'])) {
         $data['authorMail'] = $mapping['author_mail'];
     }
     // Author homepage.
     if (!empty($mapping['author_url'])) {
         $data['authorUrl'] = $mapping['author_url'];
     }
     // Author OpenID.
     if (!empty($mapping['author_openid'])) {
         $data['authorOpenid'] = $mapping['author_openid'];
     }
     // Author IP.
     $data['authorIp'] = \Drupal::request()->getClientIp();
     $mollom_form = $form_state->getValue('mollom');
     // Honeypot.
     // For the Mollom backend, it only matters whether 'honeypot' is non-empty.
     // The submitted value is only taken over to allow site administrators to
     // see the actual honeypot value in watchdog log entries.
     if (isset($mollom_form['homepage']) && $mollom_form['homepage'] !== '') {
         $data['honeypot'] = $mollom_form['homepage'];
     }
     // Add the contextCreated parameter if a callback exists.
     if (isset($mollom_form['context created callback']) && function_exists($mollom_form['context created callback'])) {
         if (!empty($mapping['context_id'])) {
             $contextCreated = call_user_func($mollom_form['context created callback'], $mapping['context_id']);
             if ($contextCreated !== FALSE) {
                 $data['contextCreated'] = $contextCreated;
             }
         }
     }
     // Ensure that all $data values contain valid UTF-8. Invalid UTF-8 would be
     // sanitized into an empty string, so the Mollom backend would not receive
     // any value.
     $invalid_utf8 = FALSE;
     $invalid_xml = FALSE;
     // Include the CAPTCHA solution user input in the UTF-8 validation.
     $solution = isset($mollom_form['captcha']['captcha_input']) ? array('solution' => $mollom_form['captcha']['captcha_input']) : array();
     foreach ($data + $solution as $key => $value) {
         // Check for invalid UTF-8 byte sequences first.
         if (!Unicode::validateUtf8($value)) {
             $invalid_utf8 = TRUE;
             // Replace the bogus string, since $data will be logged as
             // check_plain(var_export($data)), and check_plain() would empty the
             // entire exported variable string otherwise.
             $data[$key] = '- Invalid UTF-8 -';
         } elseif (preg_match('@[^\\x9\\xA\\xD\\x20-\\x{D7FF}\\x{E000}-\\x{FFFD}\\x{10000}-\\x{10FFFF}]@u', $value)) {
             $invalid_xml = TRUE;
         }
     }
     if ($invalid_utf8 || $invalid_xml) {
         $form_state->setErrorByName('', t('Your submission contains invalid characters and will not be accepted.'));
         Logger::addMessage(['message' => 'Invalid @type in form values', 'arguments' => ['@type' => $invalid_utf8 ? 'UTF-8' : 'XML characters'], 'data' => $data]);
         $data = FALSE;
     }
     return $data;
 }
Exemple #4
0
 /**
  * Tests that form errors during submission throw an exception.
  *
  * @covers ::setErrorByName
  *
  * @expectedException \LogicException
  * @expectedExceptionMessage Form errors cannot be set after form validation has finished.
  */
 public function testFormErrorsDuringSubmission()
 {
     $form_state = new FormState();
     $form_state->setValidationComplete();
     $form_state->setErrorByName('test', 'message');
 }
 /**
  * Tests selectItemAjax().
  *
  * GIVEN the embridge search form
  * WHEN the ajax handler for the select button is run with errors
  * THEN an ajax response should be returned with the expected commands.
  *
  * @covers ::selectItemAjax
  *
  * @test
  */
 public function selectItemAjaxAjaxCallbackReturnsReplaceHtmlWhenErrorsExist()
 {
     $form = ['#attached' => ['test']];
     $form_state = new FormState();
     $form_state->setErrorByName('test', 'test error');
     $response = $this->form->selectItemAjax($form, $form_state);
     $this->assertInstanceOf('\\Drupal\\Core\\Ajax\\AjaxResponse', $response);
     $commands = $response->getCommands();
     $this->assertNotEmpty($commands);
     $this->assertCount(3, $commands);
     $this->assertEquals('replaceWith', $commands[0]['method']);
     $this->assertEquals('html', $commands[1]['method']);
     $this->assertEquals('append', $commands[2]['method']);
 }
 /**
  * Test submitForm with errors.
  *
  * @covers ::ajaxSave
  *
  * @test
  */
 public function ajaxSaveWithErrorsReturnsHtmlCommand()
 {
     // #attached required for CommandWithAttachedAssetsTrait checks.
     $form = ['#attached' => []];
     $form_state = new FormState();
     $form_state->setErrorByName('test', 'ERROR ERROR!!');
     $response = $this->form->ajaxSave($form, $form_state);
     $this->assertInstanceOf('\\Drupal\\Core\\Ajax\\AjaxResponse', $response);
     $commands = $response->getCommands();
     $this->assertNotEmpty($commands);
     $this->assertCount(1, $commands);
     $this->assertEquals('insert', $commands[0]['command']);
 }