diff --git a/CRM/DataprocessorOutputExport/AbstractOutputExport.php b/CRM/DataprocessorOutputExport/AbstractOutputExport.php index 2072b86c7f9b56db89c19bf75117fc91ed8d8705..beb54fa09cf7cff6fb942ee347eec0cbfca68d73 100644 --- a/CRM/DataprocessorOutputExport/AbstractOutputExport.php +++ b/CRM/DataprocessorOutputExport/AbstractOutputExport.php @@ -30,23 +30,26 @@ abstract class CRM_DataprocessorOutputExport_AbstractOutputExport implements Exp /** * Returns the directory name for storing temporary files. * + * @param array $configuration * @return String */ - abstract public function getDirectory(): string; + abstract public function getDirectory(array $configuration=[]): string; /** * Returns the file extension. * + * @param array $configuration * @return String */ - abstract public function getExtension(): string; + abstract public function getExtension(array $configuration=[]): string; /** * Returns the mime type of the export file. * + * @param array $configuration * @return string */ - abstract public function mimeType(): string; + abstract public function mimeType(array $configuration=[]): string; /** * Run the export of the data processor. diff --git a/CRM/DataprocessorOutputExport/AbstractSpreadsheet.php b/CRM/DataprocessorOutputExport/AbstractSpreadsheet.php new file mode 100644 index 0000000000000000000000000000000000000000..77be882c5132d8cafd8d86f700545140a01c74b3 --- /dev/null +++ b/CRM/DataprocessorOutputExport/AbstractSpreadsheet.php @@ -0,0 +1,223 @@ +<?php +/** + * @author Jaap Jansma <jaap.jansma@civicoop.org> + * @license AGPL-3.0 + */ + +use Civi\DataProcessor\DataFlow\EndOfFlowException; +use Civi\DataProcessor\DataFlow\InvalidFlowException; +use Civi\DataProcessor\DataSpecification\DataSpecification; +use Civi\DataProcessor\DataSpecification\FieldExistsException; +use Civi\DataProcessor\Exception\DataFlowException; +use Civi\DataProcessor\Output\DirectDownloadExportOutputInterface; +use Civi\DataProcessor\ProcessorType\AbstractProcessorType; +use Civi\DataProcessor\FileFormat\Fileformat; +use CRM_Dataprocessor_ExtensionUtil as E; + +/** + * Abstract class to reuse for spreadsheet exports. Such as CSV and Xlsx. + */ +abstract class CRM_DataprocessorOutputExport_AbstractSpreadsheet extends CRM_DataprocessorOutputExport_AbstractOutputExport implements DirectDownloadExportOutputInterface { + + /** + * Returns the File Format class for this spreadsheet format. + * + * @param array $configuration + * @return \Civi\DataProcessor\FileFormat\Fileformat + */ + abstract protected function getFileFormatClass(array $configuration): Fileformat; + + /** + * Returns the directory name for storing temporary files. + * + * @param array $configuration + * @return String + */ + public function getDirectory(array $configuration=[]): string { + return 'dataprocessor_export_'.$this->getFileFormatClass($configuration)->getFileExtension(); + } + + /** + * Returns the file extension. + * + * @param array $configuration + * @return String + */ + public function getExtension(array $configuration=[]): string { + return $this->getFileFormatClass($configuration)->getFileExtension(); + } + + /** + * Returns the alternate file name. + * + * @param array $configuration + * @return string + */ + protected function getAlternateFileName(array $configuration=[]):? string { + if (isset($configuration['altfilename']) && ''!==$configuration['altfilename']) { + return $configuration['altfilename']; + } + return null; + } + + /** + * Get the download name of the export file. + * + * @param $dataProcessor + * @param $outputBAO + * + * @return string + */ + public function getDownloadName($dataProcessor, $outputBAO): string { + $configuration = []; + if (isset($outputBAO['configuration'])) { + $configuration = $outputBAO['configuration']; + } + $download_name = $this->getAlternateFileName($configuration); + if (empty($download_name)) { + $download_name = date('Ymdhis') . '_' . $dataProcessor['name'] . '.' . $this->getExtension($configuration); + } + return $download_name; + } + + /** + * When this filter type has additional configuration you can add + * the fields on the form with this function. + * + * @param \CRM_Core_Form $form + * @param array $output + */ + public function buildConfigurationForm(CRM_Core_Form $form, $output=array()) { + parent::buildConfigurationForm($form, $output); + try { + $form->add('text', 'altfilename', E::ts('Alternate File Name'), []); + } catch (CRM_Core_Exception $e) { + } + $configuration = array(); + if ($output && isset($output['configuration'])) { + $configuration = $output['configuration']; + } + $defaults['altfilename'] = $this->getAlternateFileName($configuration); + $form->setDefaults($defaults); + $this->getFileFormatClass($configuration)->buildConfigurationForm($form, $configuration); + $form->assign('spreadsheet_file_format_configuration', $this->getFileFormatClass($configuration)->getConfigurationTemplateFileName()); + } + + /** + * When this filter type has configuration specify the template file name + * for the configuration form. + * + * @return false|string + */ + public function getConfigurationTemplateFileName():? string { + return "CRM/DataprocessorOutputExport/Form/Configuration/Spreadsheet.tpl"; + } + + + /** + * Process the submitted values and create a configuration array + * + * @param $submittedValues + * @param array $output + * @return array + */ + public function processConfiguration($submittedValues, &$output): array { + $configuration = parent::processConfiguration($submittedValues, $output); + $this->getFileFormatClass($configuration)->processConfiguration($submittedValues, $configuration); + + if (isset($submittedValues['altfilename']) && ''!==$submittedValues['altfilename']) { + $configuration['altfilename'] = CRM_Utils_String::munge($this->removeFileExtensionFromFileName($submittedValues['altfilename'])) . '.' . $this->getExtension($configuration); + } + return $configuration; + } + + /** + * This function is called prior to removing an output + * + * @param array $output + * @return void + */ + public function deleteOutput($output) { + // Do nothing + } + + + /** + * Returns the mime type of the export file. + * + * @param array $configuration + * @return string + */ + public function mimeType(array $configuration=[]): string { + return $this->getFileFormatClass($configuration)->getMimetype(); + } + + /** + * Returns the url for the page/form this output will show to the user + * + * @param array $output + * @param array $dataProcessor + * @return string + */ + public function getTitleForExport($output, $dataProcessor): string { + return E::ts('Download as CSV'); + } + + /** + * Returns the url for the page/form this output will show to the user + * + * @param array $output + * @param array $dataProcessor + * @return string|false + */ + public function getExportFileIcon($output, $dataProcessor):? string { + $configuration = []; + if (isset($output['configuration'])) { + $configuration = $output['configuration']; + } + return $this->getFileFormatClass($configuration)->getExportFileIcon(); + } + + protected function createHeader($filename, AbstractProcessorType $dataProcessorClass, $configuration, $dataProcessor, $idField=null, $selectedIds=array(), $formValues=array()) { + $fields = new DataSpecification(); + try { + $fields = $dataProcessorClass->getDataFlow()->getDataSpecification(); + } catch (InvalidFlowException|FieldExistsException $e) { + } + + $this->getFileFormatClass($configuration)->createHeader($fields, $filename, $configuration); + } + + protected function exportRecords(string $filename, AbstractProcessorType $dataProcessor, array $configuration, string $idField=null, array $selectedIds=array(), array $formValues=array()) { + $fields = new DataSpecification(); + try { + $fields = $dataProcessor->getDataFlow()->getDataSpecification(); + } catch (InvalidFlowException|FieldExistsException $e) { + } + + try { + while($record = $dataProcessor->getDataFlow()->nextRecord()) { + $rowIsSelected = true; + if (isset($idField) && is_array($selectedIds) && count($selectedIds)) { + $rowIsSelected = false; + $id = $record[$idField]->rawValue; + if (in_array($id, $selectedIds)) { + $rowIsSelected = true; + } + } + if ($rowIsSelected) { + $row = []; + foreach ($record as $column => $value) { + $row[$column] = $value->formattedValue; + } + $this->getFileFormatClass($configuration)->addRecord($fields, $filename, $row, $configuration); + } + } + } catch (EndOfFlowException|InvalidFlowException|DataFlowException $e) { + // Do nothing + } + $this->getFileFormatClass($configuration)->closeFile($fields, $filename, $configuration); + } + + +} diff --git a/CRM/DataprocessorOutputExport/CSV.php b/CRM/DataprocessorOutputExport/CSV.php index eb89828d10a134d7920cda86dc12ba84864b0f09..3185f8021d24cd0c931a4a324968269af6b2e40e 100644 --- a/CRM/DataprocessorOutputExport/CSV.php +++ b/CRM/DataprocessorOutputExport/CSV.php @@ -4,201 +4,39 @@ * @license AGPL-3.0 */ -use Civi\DataProcessor\DataFlow\EndOfFlowException; -use Civi\DataProcessor\DataFlow\InvalidFlowException; -use Civi\DataProcessor\DataSpecification\DataSpecification; -use Civi\DataProcessor\DataSpecification\FieldExistsException; -use Civi\DataProcessor\Exception\DataFlowException; -use Civi\DataProcessor\Output\DirectDownloadExportOutputInterface; -use Civi\DataProcessor\ProcessorType\AbstractProcessorType; use Civi\DataProcessor\FileFormat\Fileformat; -use CRM_Dataprocessor_ExtensionUtil as E; -class CRM_DataprocessorOutputExport_CSV extends CRM_DataprocessorOutputExport_AbstractOutputExport implements DirectDownloadExportOutputInterface { +class CRM_DataprocessorOutputExport_CSV extends CRM_DataprocessorOutputExport_AbstractSpreadsheet { /** @var Fileformat */ private $fileFormatClass; - protected function getFileFormatClass(): Fileformat { + /** + * Returns the File Format class for this spreadsheet format. + * + * @param array $configuration + * @return \Civi\DataProcessor\FileFormat\Fileformat + */ + protected function getFileFormatClass(array $configuration=[]): Fileformat { if (empty($this->fileFormatClass)) { $factory = dataprocessor_get_factory(); $this->fileFormatClass = $factory->getFileFormatByName('csv'); } return $this->fileFormatClass; } - - /** - * Returns the directory name for storing temporary files. - * - * @return String - */ - public function getDirectory(): string { - return 'dataprocessor_export_csv'; - } - - /** - * Returns the file extension. - * - * @return String - */ - public function getExtension(): string { - return 'csv'; - } - /** - * Get the download name of the export file. - * - * @param $dataProcessor - * @param $outputBAO + * Returns the alternate file name. * + * @param array $configuration * @return string */ - public function getDownloadName($dataProcessor, $outputBAO): string { - if (isset($outputBAO['configuration']['altcsvfilename']) && ''!==$outputBAO['configuration']['altcsvfilename']) { - $download_name = $outputBAO['configuration']['altcsvfilename']; - } else { - $download_name = date('Ymdhis') . '_' . $dataProcessor['name'] . '.' . $this->getExtension(); + protected function getAlternateFileName(array $configuration=[]):? string { + if (isset($configuration['altfilename']) && ''!==$configuration['altfilename']) { + return $configuration['altfilename']; + } elseif (isset($configuration['altcsvfilename']) && ''!==$configuration['altcsvfilename']) { + return $configuration['altcsvfilename']; } - return $download_name; + return null; } - /** - * When this filter type has additional configuration you can add - * the fields on the form with this function. - * - * @param \CRM_Core_Form $form - * @param array $output - */ - public function buildConfigurationForm(CRM_Core_Form $form, $output=array()) { - parent::buildConfigurationForm($form, $output); - try { - $form->add('text', 'altcsvfilename', E::ts('Alternate CSV File Name'), []); - } catch (CRM_Core_Exception $e) { - } - $configuration = array(); - if ($output && isset($output['configuration'])) { - $configuration = $output['configuration']; - } - if (isset($configuration['altcsvfilename']) && $configuration['altcsvfilename']) { - $defaults['altcsvfilename'] = $configuration['altcsvfilename']; - } else { - $defaults['altcsvfilename'] = ''; - } - $form->setDefaults($defaults); - $this->getFileFormatClass()->buildConfigurationForm($form, $configuration); - $form->assign('csv_file_format_configuration', $this->fileFormatClass->getConfigurationTemplateFileName()); - } - - /** - * When this filter type has configuration specify the template file name - * for the configuration form. - * - * @return false|string - */ - public function getConfigurationTemplateFileName():? string { - return "CRM/DataprocessorOutputExport/Form/Configuration/CSV.tpl"; - } - - - /** - * Process the submitted values and create a configuration array - * - * @param $submittedValues - * @param array $output - * @return array - */ - public function processConfiguration($submittedValues, &$output): array { - $configuration = parent::processConfiguration($submittedValues, $output); - - if (isset($submittedValues['altcsvfilename']) && ''!==$submittedValues['altcsvfilename']) { - $configuration['altcsvfilename'] = CRM_Utils_String::munge($this->removeFileExtensionFromFileName($submittedValues['altcsvfilename'])) . '.' . $this->getExtension(); - } - $this->getFileFormatClass()->processConfiguration($submittedValues, $configuration); - return $configuration; - } - - /** - * This function is called prior to removing an output - * - * @param array $output - * @return void - */ - public function deleteOutput($output) { - // Do nothing - } - - - /** - * Returns the mime type of the export file. - * - * @return string - */ - public function mimeType(): string { - return 'text/csv'; - } - - /** - * Returns the url for the page/form this output will show to the user - * - * @param array $output - * @param array $dataProcessor - * @return string - */ - public function getTitleForExport($output, $dataProcessor): string { - return E::ts('Download as CSV'); - } - - /** - * Returns the url for the page/form this output will show to the user - * - * @param array $output - * @param array $dataProcessor - * @return string|false - */ - public function getExportFileIcon($output, $dataProcessor):? string { - return '<i class="fa fa-file-excel-o"> </i>'; - } - - protected function createHeader($filename, AbstractProcessorType $dataProcessorClass, $configuration, $dataProcessor, $idField=null, $selectedIds=array(), $formValues=array()) { - $fields = new DataSpecification(); - try { - $fields = $dataProcessorClass->getDataFlow()->getDataSpecification(); - } catch (InvalidFlowException|FieldExistsException $e) { - } - - $this->getFileFormatClass()->createHeader($fields, $filename, $configuration); - } - - protected function exportRecords(string $filename, AbstractProcessorType $dataProcessor, array $configuration, string $idField=null, array $selectedIds=array(), array $formValues=array()) { - $fields = new DataSpecification(); - try { - $fields = $dataProcessor->getDataFlow()->getDataSpecification(); - } catch (InvalidFlowException|FieldExistsException $e) { - } - - try { - while($record = $dataProcessor->getDataFlow()->nextRecord()) { - $rowIsSelected = true; - if (isset($idField) && is_array($selectedIds) && count($selectedIds)) { - $rowIsSelected = false; - $id = $record[$idField]->rawValue; - if (in_array($id, $selectedIds)) { - $rowIsSelected = true; - } - } - if ($rowIsSelected) { - $row = []; - foreach ($record as $column => $value) { - $row[$column] = $value->formattedValue; - } - $this->getFileFormatClass()->addRecord($fields, $filename, $row, $configuration); - } - } - } catch (EndOfFlowException|InvalidFlowException|DataFlowException $e) { - // Do nothing - } - $this->getFileFormatClass()->closeFile($fields, $filename, $configuration); - } - - } diff --git a/CRM/DataprocessorOutputExport/PDF.php b/CRM/DataprocessorOutputExport/PDF.php index 67c0f026e18e58f50437014ff92cfe10ec896082..6f01177672abca388850e95ae8e8ae04ff40b4c5 100644 --- a/CRM/DataprocessorOutputExport/PDF.php +++ b/CRM/DataprocessorOutputExport/PDF.php @@ -16,18 +16,20 @@ class CRM_DataprocessorOutputExport_PDF extends CRM_DataprocessorOutputExport_Ab /** * Returns the directory name for storing temporary files. * + * @param array $configuration * @return String */ - public function getDirectory(): string { + public function getDirectory(array $configuration=[]): string { return 'dataprocessor_export_pdf'; } /** * Returns the file extension. * + * @param array $configuration * @return String */ - public function getExtension(): string { + public function getExtension(array $configuration=[]): string { return 'pdf'; } @@ -224,9 +226,10 @@ class CRM_DataprocessorOutputExport_PDF extends CRM_DataprocessorOutputExport_Ab /** * Returns the mime type of the export file. * + * @param array $configuration * @return string */ - public function mimeType(): string { + public function mimeType(array $configuration=[]): string { return 'application/pdf'; } diff --git a/Civi/DataProcessor/FileFormat/CSVFileFormat.php b/Civi/DataProcessor/FileFormat/CSVFileFormat.php index 28742082a3691a7548a0d3959002d18c80b28780..47f3965e1eb1e946bdd23952c8a3911c5ae243a5 100644 --- a/Civi/DataProcessor/FileFormat/CSVFileFormat.php +++ b/Civi/DataProcessor/FileFormat/CSVFileFormat.php @@ -44,6 +44,14 @@ class CSVFileFormat implements Fileformat { return 'csv'; } + /** + * @return string + */ + public function getExportFileIcon(): string { + return '<i class="fa fa-file-excel-o"> </i>'; + } + + /** * Returns true when this configuration has additional configuration. * diff --git a/Civi/DataProcessor/FileFormat/Fileformat.php b/Civi/DataProcessor/FileFormat/Fileformat.php index 0f1d355430f0c6ef801bad74817c87a091791744..aab8c98118cba35fe00b0fe7d32d5226c66fa1d1 100644 --- a/Civi/DataProcessor/FileFormat/Fileformat.php +++ b/Civi/DataProcessor/FileFormat/Fileformat.php @@ -33,6 +33,11 @@ interface Fileformat { */ public function getFileExtension(): string; + /** + * @return string + */ + public function getExportFileIcon(): string; + /** * Returns true when this configuration has additional configuration. * diff --git a/templates/CRM/DataprocessorOutputExport/Form/Configuration/CSV.tpl b/templates/CRM/DataprocessorOutputExport/Form/Configuration/Spreadsheet.tpl similarity index 64% rename from templates/CRM/DataprocessorOutputExport/Form/Configuration/CSV.tpl rename to templates/CRM/DataprocessorOutputExport/Form/Configuration/Spreadsheet.tpl index 9a4e55848ccc3c392b32c57022d3401dd4ec8946..1611a00c4d476bda94407af230e6c39fdfa2c352 100644 --- a/templates/CRM/DataprocessorOutputExport/Form/Configuration/CSV.tpl +++ b/templates/CRM/DataprocessorOutputExport/Form/Configuration/Spreadsheet.tpl @@ -1,16 +1,16 @@ {crmScope extensionKey='dataprocessor'} <div class="crm-section"> - <div class="label">{$form.altcsvfilename.label}</div> - <div class="content">{$form.altcsvfilename.html}</div> + <div class="label">{$form.altfilename.label}</div> + <div class="content">{$form.altfilename.html}</div> <div class="clear"></div> </div> - {include file=$csv_file_format_configuration} + {include file=$spreadsheet_file_format_configuration} <div class="crm-section"> <div class="label">{$form.anonymous.label}</div> <div class="content">{$form.anonymous.html} <p class="description"> - {ts}Tick this box when you want to make the CSV available for non-logged in users. <br> - This could be necessary when another system is importing this csv file on a regular basis. E.g. a website with + {ts}Tick this box when you want to make the file available for non-logged in users. <br> + This could be necessary when another system is importing this file on a regular basis. E.g. a website with a public agenda of the upcoming events. <br><strong>Caution:</strong> when you check this box the data becomes available without logging so this might lead to a data breach.{/ts}</p> </div>