Problem/Motivation

A few years ago the method attachTo() was added to the ExcelExport style plugin, but now that there is an ExcelExport display plugin, it's not always being invoked. The DataExport display plugin from views_data_export calls it, but the ExcelExport display plugin does not.

So if you are trying to use the extra features of the ExcelExport display plugin, you can no longer attach the download to the main view. Weirdly enough, the 'display' option that powers all of this is already defined in ExcelExport::defineOptions()

Steps to reproduce

Use the ExcelExport display plugin and notice the "attach to" option is missing.

Proposed resolution

Copy the relevant code from DataExport into ExcelExport. Perhaps even consider making ExcelExport extend DataExport.

Remaining tasks

Carry out the work.

User interface changes

Attach to option will show up.

API changes

N/A

Data model changes

N/A

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

kristiaanvandeneynde created an issue. See original summary.

kristiaanvandeneynde’s picture

Status: Active » Needs review

mably made their first commit to this issue’s fork.

mably’s picture

Added a commit to disable the attachment feature if the Views Data Export module is not enabled.

Wondering why we have that dependency on the Views Data Export module in the Excel Export style in the first place...

kristiaanvandeneynde’s picture

Wondering why we have that dependency on the Views Data Export module in the Excel Export style in the first place...

Wasn't this module split off from VDE? I could be misremembering things, but that might explain it. Either way, it seems to very often be used alongside VDE, so that's why I suggested to integrate more closely with it to lower the footprint of this module and get the VDE features for free without having to maintain them.

mably’s picture

What about creating a Views Data Export Excel module? What do you think?

kristiaanvandeneynde’s picture

That could work, but the goal is to avoid fragmentation. If you're going to maintain the same code in multiple (sub)modules, then you really want to use traits or a shared API (services) module. Otherwise, maintaining will become a burden.

I reckon the structure could look as follows:

  • xls_serialization (main module, takes care of conversion to Excel)
  • xls_serialization_rest (submodule, direct integration with rest)
  • xls_serialization_vde (submodule, integrates with VDE)

But then again, VDE extends rest already and will see new features go in you will probably benefit from. So, IMO, a direct Rest integration is perhaps a waste of resources that could have gone into piggybacking on top of VDE's classes. If I were you, I'd go all in on extending VDE and taking the free wins they hand you when they fix bugs or add new features.

kristiaanvandeneynde’s picture

Oh, one thing to add, though, is that plugin implementations are considered internal. So, technically, extending the display and style plugin from VDE is not considered supported. You could reach out and ask whether they want to "officially" support this, though.

mably’s picture

I would like to avoid creating a strong dependency between between XLS Serialization and Views Data Export as some people might only be interested in the Excel serializer don't want the burden of Views Data Export and all its dependencies like CSV Serialization.

I would rather create a new contrib module depending on XLS Serialization and Views Data Export.

Pushed an MR adding two traits allowing to avoid code duplication in this new module: #3561090: Use traits for style and display plugins to facilitate code reuse.

I have something working that requires only a few lines of code.

kristiaanvandeneynde’s picture

It's your call and I can see why you'd not want to rely on VDE. I'm curious to see how the trait would interact when extending VDE's plugins.

The only UX concern I have is that people using VDE would download this module to have Excel exports and then find out that they can't use a VDE display for Excel because the one from this module has more features. In that scenario, we'd see fragmentation of the landscape and many contrib modules that add a new export type copying code from one another.

So in an ideal world, when already using VDE, you'd use a VDE display and get all the features that people who want to use this module without VDE get. However, at that point you might ask yourself why you're still maintaining a display of your own if it's only ever going to offer a subset of what the VDE+XLS display would have to offer.

To play the devil's advocate: How many people are using Views to export data without relying on VDE nowadays? It has 100k reported installs. Do you really want to compete with that when you could be using their code for free? One could also question how much a module named xls_serialization could grow into a proper rest extension without getting to a point where it has to solve problems that VDE has already solved and then become bloated with code it's not really supposed to care about.

Reason I'm asking these questions is because I was in a similar spot 13 years ago when starting to write Group when OG already existed and it was a really painful journey.

mably’s picture

I have created the Views Data Export Excel module.

Here is the related code for both the new style and display, there is almost no duplication of code:

<?php

namespace Drupal\views_data_export_excel\Plugin\views\style;

use Drupal\views_data_export\Plugin\views\style\DataExport;
use Drupal\xls_serialization\Plugin\views\style\ExcelExportStyleTrait;

/**
 * A style plugin for Excel export views.
 *
 * @ingroup views_style_plugins
 *
 * @ViewsStyle(
 *   id = "data_export_excel",
 *   title = @Translation("Data Export Excel"),
 *   help = @Translation("Configurable row output for Excel exports."),
 *   display_types = {"data"}
 * )
 */
class ExcelExport extends DataExport {

  use ExcelExportStyleTrait;

  /**
   * Constructs a Plugin object.
   *
   * @param array $configuration
   *   A configuration array containing information about the plugin instance.
   * @param string $plugin_id
   *   The plugin_id for the plugin instance.
   * @param mixed $plugin_definition
   *   The plugin implementation definition.
   * @param mixed $serializer
   *   The serializer for the plugin instance.
   * @param array $serializer_formats
   *   The serializer formats for the plugin instance.
   * @param array $serializer_format_providers
   *   The serializer format providers for the plugin instance.
   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
   *   The module handler service.
   */
  public function __construct(
    array $configuration,
    $plugin_id,
    $plugin_definition,
    $serializer,
    array $serializer_formats,
    array $serializer_format_providers,
    $module_handler,
  ) {
    parent::__construct(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $serializer,
      $serializer_formats,
      $serializer_format_providers,
      $module_handler,
    );

    $this->initializeSerializerFormats();
  }

}
<?php

namespace Drupal\views_data_export_excel\Plugin\views\display;

use Drupal\Core\Form\FormStateInterface;
use Drupal\views_data_export\Plugin\views\display\DataExport;
use Drupal\xls_serialization\Plugin\views\display\ExcelExportDisplayTrait;

/**
 * Provides an Excel export display plugin.
 *
 * This overrides the Data Export display to make labeling clearer on the admin
 * UI, and add specific Excel-related functionality.
 *
 * @ingroup views_display_plugins
 *
 * @ViewsDisplay(
 *   id = "data_export_excel",
 *   title = @Translation("Data Export Excel"),
 *   help = @Translation("Export the view results to an Excel file."),
 *   uses_route = TRUE,
 *   admin = @Translation("Data Export Excel"),
 *   returns_response = TRUE
 * )
 */
class ExcelExport extends DataExport {

  use ExcelExportDisplayTrait;

  /**
   * Overrides the content type of the data response.
   *
   * @var string
   */
  protected $contentType = 'xlsx';

  /**
   * {@inheritdoc}
   */
  public function buildOptionsForm(&$form, FormStateInterface $form_state) {
    parent::buildOptionsForm($form, $form_state);

    switch ($form_state->get('section')) {
      case 'style':
        $this->buildStyleSectionForm($form, $form_state);
        break;

      case 'path':
        // Use default Views Data Export form.
        break;

      case 'format_header':
        $this->buildFormatHeaderSectionForm($form, $form_state);
        break;
    }

    $this->buildConditionalFormattingRulesForm($form, $form_state);
  }

}

Could you give it a try and tell me how it goes for you?

mably’s picture

Views Data Export Excel 1.0.0-rc1 has been released.

If it solves the problem we will be able to mark this issue's as "Won't fix".

mably’s picture

Status: Needs review » Closed (won't fix)

Views Data Export 1.0.0 has been released. Closing.

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.