diff --git a/core/modules/rest/lib/Drupal/rest/Plugin/views/row/DataFieldRow.php b/core/modules/rest/lib/Drupal/rest/Plugin/views/row/DataFieldRow.php index 97541f6d72c70b1564d597d028d6d433d6d7ad65..6eb9b22cdbc1cbc6ae60e7b1fb6c42557d5a2784 100644 --- a/core/modules/rest/lib/Drupal/rest/Plugin/views/row/DataFieldRow.php +++ b/core/modules/rest/lib/Drupal/rest/Plugin/views/row/DataFieldRow.php @@ -40,15 +40,26 @@ class DataFieldRow extends RowPluginBase { */ protected $replacementAliases = array(); + /** + * Stores an array of options to determine if the raw field output is used. + * + * @var array + */ + protected $rawOutputOptions = array(); + /** * Overrides \Drupal\views\Plugin\views\row\RowPluginBase::init(). */ public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) { parent::init($view, $display, $options); - if (!empty($this->options['aliases'])) { + if (!empty($this->options['field_options'])) { + $options = (array) $this->options['field_options']; // Prepare a trimmed version of replacement aliases. - $this->replacementAliases = array_filter(array_map('trim', (array) $this->options['aliases'])); + $aliases = static::extractFromOptionsArray('alias', $options); + $this->replacementAliases = array_filter(array_map('trim', $aliases)); + // Prepare an array of raw output field options. + $this->rawOutputOptions = static::extractFromOptionsArray('raw_output', $options); } } @@ -57,7 +68,7 @@ public function init(ViewExecutable $view, DisplayPluginBase $display, array &$o */ protected function defineOptions() { $options = parent::defineOptions(); - $options['aliases'] = array('default' => array()); + $options['field_options'] = array('default' => array()); return $options; } @@ -69,21 +80,29 @@ protected function defineOptions() { public function buildOptionsForm(&$form, &$form_state) { parent::buildOptionsForm($form, $form_state); - $form['aliases'] = array( - '#type' => 'fieldset', - '#title' => t('Field ID aliases'), - '#description' => t('Rename views default field IDs in the output data.'), + $form['field_options'] = array( + '#type' => 'table', + '#header' => array(t('Field'), t('Alias'), t('Raw output')), + '#empty' => t('You have no fields. Add some to your view.'), '#tree' => TRUE, ); + $options = $this->options['field_options']; + if ($fields = $this->view->display_handler->getOption('fields')) { foreach ($fields as $id => $field) { - $form['aliases'][$id] = array( + $form['field_options'][$id]['field'] = array( + '#markup' => $id, + ); + $form['field_options'][$id]['alias'] = array( '#type' => 'textfield', - '#title' => $id, - '#default_value' => isset($this->options['aliases'][$id]) ? $this->options['aliases'][$id] : '', + '#default_value' => isset($options[$id]['alias']) ? $options[$id]['alias'] : '', '#element_validate' => array(array($this, 'validateAliasName')), ); + $form['field_options'][$id]['raw_output'] = array( + '#type' => 'checkbox', + '#default_value' => isset($options[$id]['raw_output']) ? $options[$id]['raw_output'] : '', + ); } } } @@ -101,10 +120,12 @@ public function validateAliasName($element, &$form_state) { * Overrides \Drupal\views\Plugin\views\row\RowPluginBase::validateOptionsForm(). */ public function validateOptionsForm(&$form, &$form_state) { - $aliases = $form_state['values']['row_options']['aliases']; + // Collect an array of aliases to validate. + $aliases = static::extractFromOptionsArray('alias', $form_state['values']['row_options']['field_options']); + // If array filter returns empty, no values have been entered. Unique keys // should only be validated if we have some. - if (array_filter($aliases) && (array_unique($aliases) !== $aliases)) { + if (($filtered = array_filter($aliases)) && (array_unique($filtered) !== $filtered)) { form_set_error('aliases', t('All field aliases must be unique')); } } @@ -116,14 +137,14 @@ public function render($row) { $output = array(); foreach ($this->view->field as $id => $field) { - // If we don't have a field alias, Just try to get the rendered output - // from the field. - if ($field->field_alias == 'unknown') { - $value = $field->render($row); + // If this is not unknown and the raw output option has been set, just get + // the raw value. + if (($field->field_alias != 'unknown') && !empty($this->rawOutputOptions[$id])) { + $value = $field->sanitizeValue($field->get_value($row), 'xss_admin'); } - // Get the value directly from the result row. + // Otherwise, pass this through the field render() method. else { - $value = $row->{$field->field_alias}; + $value = $field->render($row); } $output[$this->getFieldKeyAlias($id)] = $value; @@ -149,4 +170,21 @@ public function getFieldKeyAlias($id) { return $id; } + /** + * Extracts a set of option values from a nested options array. + * + * @param string $key + * The key to extract from each array item. + * @param array $options + * The options array to return values from. + * + * @return array + * A regular one dimensional array of values. + */ + protected static function extractFromOptionsArray($key, $options) { + return array_map(function($item) use ($key) { + return isset($item[$key]) ? $item[$key] : NULL; + }, $options); + } + } diff --git a/core/modules/rest/lib/Drupal/rest/Tests/Views/StyleSerializerTest.php b/core/modules/rest/lib/Drupal/rest/Tests/Views/StyleSerializerTest.php index 9bdfbbea2c26abf63050c457faa8b42c7bf67b00..5c4f9bd6932b128d54ce1f5bf7b7a992fc7353e1 100644 --- a/core/modules/rest/lib/Drupal/rest/Tests/Views/StyleSerializerTest.php +++ b/core/modules/rest/lib/Drupal/rest/Tests/Views/StyleSerializerTest.php @@ -82,12 +82,7 @@ public function testSerializerResponses() { foreach ($view->result as $row) { $expected_row = array(); foreach ($view->field as $id => $field) { - if ($field->field_alias == 'unknown') { - $expected_row[$id] = $field->render($row); - } - else { - $expected_row[$id] = $row->{$field->field_alias}; - } + $expected_row[$id] = $field->render($row); } $expected[] = $expected_row; } @@ -146,7 +141,7 @@ public function testUIFieldAlias() { // Test an empty string for an alias, this should not be used. This also // tests that the form can be submitted with no aliases. - $this->drupalPost($row_options, array('row_options[aliases][name]' => ''), t('Apply')); + $this->drupalPost($row_options, array('row_options[field_options][name][alias]' => ''), t('Apply')); $this->drupalPost(NULL, array(), t('Save')); $view = views_get_view('test_serializer_display_field'); @@ -157,13 +152,7 @@ public function testUIFieldAlias() { foreach ($view->result as $row) { $expected_row = array(); foreach ($view->field as $id => $field) { - // Original field key is expected. - if ($field->field_alias == 'unknown') { - $expected_row[$id] = $field->render($row); - } - else { - $expected_row[$id] = $row->{$field->field_alias}; - } + $expected_row[$id] = $field->render($row); } $expected[] = $expected_row; } @@ -172,35 +161,34 @@ public function testUIFieldAlias() { $this->assertIdentical($this->drupalGetAJAX('test/serialize/field'), $expected); // Test a random aliases for fields, they should be replaced. - $random_name = $this->randomName(); - // Use # to produce an invalid character for the validation. - $invalid_random_name = '#' . $this->randomName(); - $edit = array('row_options[aliases][name]' => $random_name, 'row_options[aliases][nothing]' => $invalid_random_name); + $alias_map = array( + 'name' => $this->randomName(), + // Use # to produce an invalid character for the validation. + 'nothing' => '#' . $this->randomName(), + 'created' => 'created', + ); + + $edit = array('row_options[field_options][name][alias]' => $alias_map['name'], 'row_options[field_options][nothing][alias]' => $alias_map['nothing']); $this->drupalPost($row_options, $edit, t('Apply')); $this->assertText(t('The machine-readable name must contain only letters, numbers, dashes and underscores.')); - $random_name_custom = $this->randomName(); - $edit = array('row_options[aliases][name]' => $random_name, 'row_options[aliases][nothing]' => $random_name_custom); + // Change the map alias value to a valid one. + $alias_map['nothing'] = $this->randomName(); + + $edit = array('row_options[field_options][name][alias]' => $alias_map['name'], 'row_options[field_options][nothing][alias]' => $alias_map['nothing']); $this->drupalPost($row_options, $edit, t('Apply')); $this->drupalPost(NULL, array(), t('Save')); $view = views_get_view('test_serializer_display_field'); - $view->setDisplay('ws_endpoint_1'); + $view->setDisplay('rest_export_1'); $this->executeView($view); $expected = array(); foreach ($view->result as $row) { $expected_row = array(); foreach ($view->field as $id => $field) { - // This will be the custom field. - if ($field->field_alias == 'unknown') { - $expected_row[$random_name_custom] = $field->render($row); - } - // This will be the name field. - else { - $expected_row[$random_name] = $row->{$field->field_alias}; - } + $expected_row[$alias_map[$id]] = $field->render($row); } $expected[] = $expected_row; } @@ -208,4 +196,30 @@ public function testUIFieldAlias() { $this->assertIdentical($this->drupalGetAJAX('test/serialize/field'), $expected); } + /** + * Tests the raw output options for row field rendering. + */ + public function testFieldRawOutput() { + $this->drupalLogin($this->adminUser); + + // Test the UI settings for adding field ID aliases. + $this->drupalGet('admin/structure/views/view/test_serializer_display_field/edit/rest_export_1'); + $row_options = 'admin/structure/views/nojs/display/test_serializer_display_field/rest_export_1/row_options'; + $this->assertLinkByHref($row_options); + + // Test an empty string for an alias, this should not be used. This also + // tests that the form can be submitted with no aliases. + $this->drupalPost($row_options, array('row_options[field_options][created][raw_output]' => '1'), t('Apply')); + $this->drupalPost(NULL, array(), t('Save')); + + $view = views_get_view('test_serializer_display_field'); + $view->setDisplay('rest_export_1'); + $this->executeView($view); + + // Just test the raw 'created' value against each row. + foreach ($this->drupalGetAJAX('test/serialize/field') as $index => $values) { + $this->assertIdentical($values['created'], $view->result[$index]->views_test_data_created, 'Expected raw created value found.'); + } + } + } diff --git a/core/modules/rest/tests/modules/rest_test_views/test_views/views.view.test_serializer_display_field.yml b/core/modules/rest/tests/modules/rest_test_views/test_views/views.view.test_serializer_display_field.yml index 7650d521a87194ca37e3f154cdcebe02982f6f49..11494766a6830b16a925557af0c7fc5de7fc204e 100644 --- a/core/modules/rest/tests/modules/rest_test_views/test_views/views.view.test_serializer_display_field.yml +++ b/core/modules/rest/tests/modules/rest_test_views/test_views/views.view.test_serializer_display_field.yml @@ -45,6 +45,11 @@ display: alter_text: '1' text: TEST plugin_id: custom + created: + id: created + table: views_test_data + field: created + plugin_id: date sorts: created: id: created diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php index 4383bd4d8cade4c7e80c92a99ff4c8a5812c18fb..a96bc47da492ff09a8e6f8c167959b510494e034 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php @@ -208,7 +208,7 @@ public function getField($field = NULL) { * @return string * Returns the safe value. */ - protected function sanitizeValue($value, $type = NULL) { + public function sanitizeValue($value, $type = NULL) { switch ($type) { case 'xss': $value = filter_xss($value);