View source
<?php
namespace Drupal\webform_scheduled_tasks\Plugin\WebformScheduledTasks\Task;
use Drupal\Core\Archiver\ArchiveTar;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\SubformState;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Mail\MailManagerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\file\Entity\File;
use Drupal\file\FileUsage\FileUsageInterface;
use Drupal\webform\Entity\WebformSubmission;
use Drupal\webform\Plugin\WebformExporterManagerInterface;
use Drupal\webform\WebformSubmissionExporterInterface;
use Drupal\webform_scheduled_tasks\Exception\HaltScheduledTaskException;
use Drupal\webform_scheduled_tasks\Plugin\WebformScheduledTasks\TaskPluginBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
class EmailedExport extends TaskPluginBase implements ContainerFactoryPluginInterface {
protected $exporter;
protected $exporterManager;
protected $mailManager;
protected $fileUsage;
protected $fileSystem;
const STORAGE_TYPE_EMAIL = 'email';
const STORAGE_TYPE_FILESYSTEM = 'filesystem';
const DESTINATION_DIRECTORY = 'private://scheduled-exports';
public function __construct(array $configuration, $plugin_id, $plugin_definition, WebformSubmissionExporterInterface $exporter, WebformExporterManagerInterface $exporterManager, MailManagerInterface $mailManager, FileUsageInterface $fileUsage, FileSystemInterface $fileSystem) {
$this->exporter = $exporter;
$this->exporterManager = $exporterManager;
$this->mailManager = $mailManager;
$this->fileUsage = $fileUsage;
$this->fileSystem = $fileSystem;
parent::__construct($configuration, $plugin_id, $plugin_definition);
}
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($configuration, $plugin_id, $plugin_definition, $container
->get('webform_submission.exporter'), $container
->get('plugin.manager.webform.exporter'), $container
->get('plugin.manager.mail'), $container
->get('file.usage'), $container
->get('file_system'));
}
public function defaultConfiguration() {
$exporter_options = $this->exporterManager
->getOptions();
return [
'exporter_settings' => [],
'exporter' => key($exporter_options),
'email_addresses' => '',
'storage_type' => static::STORAGE_TYPE_FILESYSTEM,
'delete_submissions' => FALSE,
'include_attachments' => TRUE,
];
}
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form['email_addresses'] = [
'#title' => $this
->t('Email addresses'),
'#required' => TRUE,
'#description' => $this
->t('Enter a list of email addresses to notify when the export is complete. The list should be coma separated values.'),
'#type' => 'textarea',
'#attributes' => [
'placeholder' => 'foo@example.com, bar@example.com',
],
'#default_value' => $this->configuration['email_addresses'],
];
$form['storage_type'] = [
'#type' => 'radios',
'#title' => $this
->t('Storage type'),
'#options' => [
static::STORAGE_TYPE_FILESYSTEM => $this
->t('Save to private filesystem'),
static::STORAGE_TYPE_EMAIL => $this
->t('Send as email attachment'),
],
'#default_value' => $this->configuration['storage_type'],
'#required' => TRUE,
'#description' => $this
->t('Select how the resulting file will be delivered to the configured users. Saving the file to the file system will generate a private file which only privileged roles will have access to.'),
];
$form['attachment_storage_type_warning'] = [
'#type' => 'webform_message',
'#message_type' => 'warning',
'#message_message' => $this
->t('<strong>Warning:</strong> Sending email file attachments requires webform to have already been configured for attachments. See <a target="_blank" href=":help">this help topic for more information</a>.', [
':help' => 'https://www.drupal.org/node/3021480',
]),
'#states' => [
'visible' => [
':input[name="task_settings[storage_type]"]' => [
'value' => 'email',
],
],
],
];
$form['delete_submissions'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Delete submissions after export'),
'#default_value' => $this->configuration['delete_submissions'],
'#description' => $this
->t('Delete submissions after this task has been run.'),
];
$form['include_attachments'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Include attachments'),
'#default_value' => $this->configuration['include_attachments'],
'#description' => $this
->t('Include attachments uploaded by users in the exported archive.'),
];
$this
->buildExportPluginForm($form, $form_state);
return $form;
}
protected function buildExportPluginForm(&$form, FormStateInterface $form_state) {
$form['exporter'] = [
'#title' => $this
->t('Export format'),
'#type' => 'select',
'#options' => $this->exporterManager
->getOptions(),
'#default_value' => $this->configuration['exporter'],
'#ajax' => [
'callback' => [
static::class,
'ajaxCallback',
],
'wrapper' => 'exporter-settings',
],
];
$chosen_exporter = $form_state
->getValue('exporter', $this->configuration['exporter']);
$plugin = $this->exporterManager
->createInstance($chosen_exporter, $this
->getConfiguration()['exporter_settings']);
$form['exporter_settings'] = [
'#prefix' => '<div id="exporter-settings">',
'#suffix' => '</div>',
];
$subform_state = SubformState::createForSubform($form['exporter_settings'], $form, $form_state);
$form['exporter_settings'] += $plugin
->buildConfigurationForm($form['exporter_settings'], $subform_state);
}
public static function ajaxCallback($form, FormStateInterface $form_state) {
return $form['task_settings']['exporter_settings'];
}
public function executeTask(\Iterator $submissions) {
$exporter = $this
->initializeExporter();
$exporter
->writeHeader();
$processed_submission_ids = [];
foreach ($submissions as $submission) {
$exporter
->writeRecords([
$submission,
]);
$processed_submission_ids[] = $submission
->id();
}
$exporter
->writeFooter();
if ($exporter
->isArchive()) {
$exporter
->writeExportToArchive();
}
if (empty($processed_submission_ids)) {
return;
}
if (!file_exists($this
->getExportFileOrArchivePath($exporter)) || filesize($this
->getExportFileOrArchivePath($exporter)) === 0) {
throw new HaltScheduledTaskException('Export files are failing to generate or are empty.');
}
if ($exporter
->isArchive() && !$this
->verifyArchive($this
->getExportFileOrArchivePath($exporter))) {
throw new HaltScheduledTaskException('An invalid archive file was generated.');
}
$target_directory = static::DESTINATION_DIRECTORY;
if (!$this->fileSystem
->prepareDirectory($target_directory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS)) {
throw new HaltScheduledTaskException('Could not create a directory for the exported files to be written to.');
}
$attempted_file_destination_uri = sprintf('%s/%s', static::DESTINATION_DIRECTORY, $this
->getExportFileOrArchiveName($exporter));
if (!($unique_destination_file_uri = $this->fileSystem
->copy($this
->getExportFileOrArchivePath($exporter), $attempted_file_destination_uri))) {
throw new HaltScheduledTaskException('Could not write the generated file to the private file system.');
}
$file = File::create([
'uri' => $unique_destination_file_uri,
'filename' => $this
->getExportFileOrArchiveName($exporter),
'status' => FILE_STATUS_PERMANENT,
]);
$file
->save();
$this->fileUsage
->add($file, 'webform_scheduled_tasks', 'email_uri', $this
->getScheduledTask()
->id());
$email_attachments = [];
if ($this->configuration['storage_type'] === static::STORAGE_TYPE_EMAIL) {
$email_attachments[] = [
'filecontent' => file_get_contents($file
->getFileUri()),
'filename' => $file
->getFilename(),
'filemime' => $file
->getMimeType(),
'filepath' => $this->fileSystem
->realpath($file
->getFileUri()),
];
}
$this
->emailRecipients('export_summary_filesystem', [
'file_url' => file_create_url($unique_destination_file_uri),
'task_id' => $this
->getScheduledTask()
->id(),
'attachments' => $email_attachments,
]);
if ($this->configuration['delete_submissions']) {
foreach ($processed_submission_ids as $submission_delete_id) {
WebformSubmission::load($submission_delete_id)
->delete();
}
}
}
protected function verifyArchive($file) {
$archive = new ArchiveTar($file);
$content = $archive
->listContent();
return !empty($content);
}
protected function getExportFileOrArchivePath(WebformSubmissionExporterInterface $initializedExporter) {
if ($initializedExporter
->isArchive()) {
return $initializedExporter
->getArchiveFilePath();
}
return $initializedExporter
->getExportFilePath();
}
protected function getExportFileOrArchiveName(WebformSubmissionExporterInterface $initializedExporter) {
if ($initializedExporter
->isArchive()) {
return $initializedExporter
->getArchiveFileName();
}
return $initializedExporter
->getExportFileName();
}
protected function emailRecipients($key, array $params) {
foreach ($this
->getEmailAddresses() as $email_address) {
$this->mailManager
->mail('webform_scheduled_tasks', $key, $email_address, LanguageInterface::LANGCODE_DEFAULT, $params);
}
}
protected function getEmailAddresses() {
return array_map('trim', explode(',', $this->configuration['email_addresses']));
}
protected function initializeExporter() {
$this->exporter
->setWebform($this
->getScheduledTask()
->getWebform());
$this->exporter
->setSourceEntity($this
->getScheduledTask());
$exporter_options = $this->configuration['exporter_settings'] + [
'exporter' => $this->configuration['exporter'],
'files' => $this->configuration['include_attachments'],
];
$this->exporter
->setExporter($exporter_options);
return $this->exporter;
}
}