Commit 8b18d5ca authored by Steven Jones's avatar Steven Jones
Browse files

Issue #3046184 by andrewbelcher, steven jones, cornifex, andrey.baranovskiy,...

Issue #3046184 by andrewbelcher, steven jones, cornifex, andrey.baranovskiy, matthijs, danielhonrade, berdir, jhedstrom, joelpittet: Optional CSV header row
parent e09f6754
Loading
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -82,6 +82,9 @@ views.style.data_export:
        use_serializer_encode_only:
          type: boolean
          label: 'Only use serializer->encode method'
        output_header:
          type: boolean
          label: 'Show header row'
    xls_settings:
      type: mapping
      label: 'XLS settings'
+5 −2
Original line number Diff line number Diff line
@@ -805,8 +805,11 @@ class DataExport extends RestExport {
    $string = (string) $rendered_rows['#markup'];

    // Workaround for CSV headers, remove the first line.
    if ($context['sandbox']['progress'] != 0 && reset($view->getStyle()->options['formats']) == 'csv') {
      $string = preg_replace('/^[^\n]+/', '', $string);
    $options = $view->getStyle()->options;
    if ($context['sandbox']['progress'] != 0 && reset($options['formats']) == 'csv') {
      $string = $options['csv_settings']['output_header']
        ? preg_replace('/^[^\n]+/', '', $string)
        : "\n{$string}";
    }

    // Workaround for XML.
+6 −0
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@ class DataExport extends Serializer {
      'encoding' => ['default' => 'utf8'],
      'utf8_bom' => ['default' => FALSE],
      'use_serializer_encode_only' => ['default' => FALSE],
      'output_header' => ['default' => TRUE],
    ];

    return $options;
@@ -135,6 +136,11 @@ class DataExport extends Serializer {
              '#description' => $this->t('Skips the symfony data normalize method when rendering data export to increase performance on large datasets. <strong>(Only use when not exporting nested data)</strong>'),
              '#default_value' => $csv_options['use_serializer_encode_only'],
            ],
            'output_header' => [
              '#type' => 'checkbox',
              '#title' => $this->t("Show the header row"),
              '#default_value' => $csv_options['output_header'],
            ],
          ];
        }
        break;
+224 −0
Original line number Diff line number Diff line
langcode: en
status: true
dependencies:
  module:
    - csv_serialization
    - node
    - rest
    - serialization
    - user
    - views_data_export
id: views_data_test_4
label: 'Views data test 4'
module: views
description: ''
tag: ''
base_table: node_field_data
base_field: nid
core: 8.x
display:
  default:
    display_plugin: default
    id: default
    display_title: Master
    position: 0
    display_options:
      access:
        type: perm
        options:
          perm: 'access content'
      cache:
        type: tag
        options: {  }
      query:
        type: views_query
        options:
          disable_sql_rewrite: false
          distinct: false
          replica: false
          query_comment: ''
          query_tags: {  }
      exposed_form:
        type: basic
        options:
          submit_button: Apply
          reset_button: false
          reset_button_label: Reset
          exposed_sorts_label: 'Sort by'
          expose_sort_order: true
          sort_asc_label: Asc
          sort_desc_label: Desc
      pager:
        type: mini
        options:
          items_per_page: 10
          offset: 0
          id: 0
          total_pages: null
          expose:
            items_per_page: false
            items_per_page_label: 'Items per page'
            items_per_page_options: '5, 10, 25, 50'
            items_per_page_options_all: false
            items_per_page_options_all_label: '- All -'
            offset: false
            offset_label: Offset
          tags:
            previous: ‹‹
            next: ››
      style:
        type: default
        options:
          grouping: {  }
          row_class: ''
          default_row_class: true
          uses_fields: false
      row:
        type: fields
        options:
          inline: {  }
          separator: ''
          hide_empty: false
          default_field_elements: true
      fields:
        title:
          id: title
          table: node_field_data
          field: title
          entity_type: node
          entity_field: title
          label: ''
          alter:
            alter_text: false
            make_link: false
            absolute: false
            trim: false
            word_boundary: false
            ellipsis: false
            strip_tags: false
            html: false
          hide_empty: false
          empty_zero: false
          settings:
            link_to_entity: true
          plugin_id: field
          relationship: none
          group_type: group
          admin_label: ''
          exclude: false
          element_type: ''
          element_class: ''
          element_label_type: ''
          element_label_class: ''
          element_label_colon: true
          element_wrapper_type: ''
          element_wrapper_class: ''
          element_default_classes: true
          empty: ''
          hide_alter_empty: true
          click_sort_column: value
          type: string
          group_column: value
          group_columns: {  }
          group_rows: true
          delta_limit: 0
          delta_offset: 0
          delta_reversed: false
          delta_first_last: false
          multi_type: separator
          separator: ', '
          field_api_classes: false
      filters:
        status:
          value: '1'
          table: node_field_data
          field: status
          plugin_id: boolean
          entity_type: node
          entity_field: status
          id: status
          expose:
            operator: ''
          group: 1
      sorts:
        created:
          id: created
          table: node_field_data
          field: created
          order: DESC
          entity_type: node
          entity_field: created
          plugin_id: date
          relationship: none
          group_type: group
          admin_label: ''
          exposed: false
          expose:
            label: ''
          granularity: second
      header: {  }
      footer: {  }
      empty: {  }
      relationships: {  }
      arguments: {  }
      display_extenders: {  }
    cache_metadata:
      max-age: -1
      contexts:
        - 'languages:language_content'
        - 'languages:language_interface'
        - url.query_args
        - 'user.node_grants:view'
        - user.permissions
      tags: {  }
  data_export_1:
    display_plugin: data_export
    id: data_export_1
    display_title: 'Data export'
    position: 1
    display_options:
      display_extenders: {  }
      path: views_data_export/test_4
      filename: test_4
      automatic_download: false
      redirect_path: ''
      style:
        type: data_export
        options:
          formats:
            csv: csv
          csv_settings:
            delimiter: ','
            enclosure: '"'
            escape_char: \
            strip_tags: true
            trim: true
            encoding: utf8
            output_header: false
          xls_settings:
            xls_format: Excel2007
            metadata:
              creator: ''
              last_modified_by: ''
              title: ''
              description: ''
              subject: ''
              keywords: ''
              category: ''
              manager: ''
              company: ''
      export_method: batch
      export_batch_size: 4
      store_in_public_file_directory: false
      redirect_to_display: none
      custom_redirect_path: false
      include_query_params: false
    cache_metadata:
      max-age: -1
      contexts:
        - 'languages:language_content'
        - 'languages:language_interface'
        - request_format
        - 'user.node_grants:view'
        - user.permissions
      tags: {  }
+26 −6
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ class ViewsDataExportBatchTest extends ViewTestBase {
    'views_data_test_1',
    'views_data_test_2',
    'views_data_test_3',
    'views_data_test_4',
  ];

  /**
@@ -82,13 +83,21 @@ class ViewsDataExportBatchTest extends ViewTestBase {
   */
  public function testBatchCreation() {

    // By this view we fetch page with link present.
    // By this view we fetch page with link present and csv with 11 rows.
    $this->drupalGet('views_data_export/test_1');
    $link = $this->getSession()->getPage()->findLink('here');
    $path_to_file = $link->getAttribute('href');
    $this->drupalGet($path_to_file);
    $this->assertEquals(200, $this->getSession()->getStatusCode(), 'File was not created');

    $path_to_file = parse_url($path_to_file, PHP_URL_PATH);
    $public_directory_path = \Drupal::service('stream_wrapper_manager')->getViaScheme('public')->getDirectoryPath();
    $path_to_file = str_replace($_SERVER['REQUEST_URI'] . $public_directory_path, 'public:/', $path_to_file);
    $res1 = $this->readCsv(file_get_contents($path_to_file));
    // The first line contains the header, expect one extra row.
    $this->assertEquals(11, count($res1), 'Count of exported nodes is wrong.');
    $this->assertEquals([0 => 'title'], $res1[0]);

    // By this view we obtain file right after batch process finished.
    // @todo make separate FunctionalJavascript test to check automatic fetching.
    $this->drupalGet('views_data_export/test_2');
@@ -105,10 +114,21 @@ class ViewsDataExportBatchTest extends ViewTestBase {
    $path_to_file = parse_url($path_to_file, PHP_URL_PATH);
    $path_to_file = str_replace($_SERVER['REQUEST_URI'] . 'system/files', 'private:/', $path_to_file);
    $res3 = $this->readCsv(file_get_contents($path_to_file));
    // The first line always contains the header, expect one extra row.
    // The first line contains the header, expect one extra row.
    $this->assertEquals(4, count($res3), 'Count of exported nodes is wrong.');
    $this->assertEquals([0 => 'title'], $res3[0]);

    // By this view's batch finished we fetch csv with 10 rows without header.
    $this->drupalGet('views_data_export/test_4');
    $link = $this->getSession()->getPage()->findLink('here');
    $path_to_file = $link->getAttribute('href');

    $path_to_file = parse_url($path_to_file, PHP_URL_PATH);
    $path_to_file = str_replace($_SERVER['REQUEST_URI'] . 'system/files', 'private:/', $path_to_file);
    $res4 = $this->readCsv(file_get_contents($path_to_file));
    $this->assertEquals(10, count($res4), 'Count of exported nodes is wrong.');
    $this->assertNotEquals([0 => 'title'], $res4[0]);

    // Testing search api index's view.
    $this->indexItems('database_search_index');
    $this->drupalGet('views_data_export/test_search_api');
@@ -117,10 +137,10 @@ class ViewsDataExportBatchTest extends ViewTestBase {
    $path_to_file = $link->getAttribute('href');
    $path_to_file = parse_url($path_to_file, PHP_URL_PATH);
    $path_to_file = str_replace($_SERVER['REQUEST_URI'] . 'system/files', 'private:/', $path_to_file);
    $res4 = $this->readCsv(file_get_contents($path_to_file));
    // The first line always contains the header, expect one extra row.
    $this->assertEquals(9, count($res4), 'Count of exported test entities is wrong.');
    $this->assertEquals([0 => 'id'], $res4[0]);
    $res5 = $this->readCsv(file_get_contents($path_to_file));
    // The first line contains the header, expect one extra row.
    $this->assertEquals(9, count($res5), 'Count of exported test entities is wrong.');
    $this->assertEquals([0 => 'id'], $res5[0]);
  }

  /**