diff --git a/dataformat/excel/tests/writer_test.php b/dataformat/excel/tests/writer_test.php new file mode 100644 index 00000000000..ec9fce708ba --- /dev/null +++ b/dataformat/excel/tests/writer_test.php @@ -0,0 +1,81 @@ +. + +namespace dataformat_excel; + +use core\dataformat; + +/** + * Tests for the dataformat_excel writer + * + * @package dataformat_excel + * @copyright 2022 Marina Glancy + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class writer_test extends \advanced_testcase { + + /** + * Test writing data whose content contains an image with pluginfile.php source + */ + public function test_write_data(): void { + $columns = ['fruit', 'colour', 'animal']; + $rows = [ + ['banana', 'yellow', 'monkey'], + ['apple', 'red', 'wolf'], + ['melon', 'green', 'aardvark'], + ]; + + // Export to file. + $exportfile = dataformat::write_data('My export', 'excel', $columns, $rows); + + // Read the file. + $excelcells = $this->get_excel(file_get_contents($exportfile)); + + $this->assertEquals(array_merge([$columns], $rows), $excelcells); + } + + /** + * Get an Excel object to check the content + * + * @param string $content + * @return array two-dimensional array with cell values + */ + private function get_excel(string $content) { + $file = tempnam(sys_get_temp_dir(), 'excel_'); + $handle = fopen($file, "w"); + fwrite($handle, $content); + /** @var \Box\Spout\Reader\XLSX\Reader $reader */ + $reader = \Box\Spout\Reader\Common\Creator\ReaderFactory::createFromType(\Box\Spout\Common\Type::XLSX); + $reader->open($file); + + /** @var \Box\Spout\Reader\XLSX\Sheet[] $sheets */ + $sheets = $reader->getSheetIterator(); + $rowscellsvalues = []; + foreach ($sheets as $sheet) { + /** @var \Box\Spout\Common\Entity\Row[] $rows */ + $rows = $sheet->getRowIterator(); + foreach ($rows as $row) { + $thisvalues = []; + foreach ($row->getCells() as $cell) { + $thisvalues[] = $cell->getValue(); + } + $rowscellsvalues[] = $thisvalues; + } + } + + return $rowscellsvalues; + } +} diff --git a/dataformat/ods/tests/writer_test.php b/dataformat/ods/tests/writer_test.php new file mode 100644 index 00000000000..fe71ab0f50b --- /dev/null +++ b/dataformat/ods/tests/writer_test.php @@ -0,0 +1,80 @@ +. + +namespace dataformat_ods; + +use core\dataformat; + +/** + * Tests for the dataformat_ods writer + * + * @package dataformat_ods + * @copyright 2022 Marina Glancy + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class writer_test extends \advanced_testcase { + + /** + * Test writing data whose content contains an image with pluginfile.php source + */ + public function test_write_data(): void { + $columns = ['fruit', 'colour', 'animal']; + $rows = [ + ['banana', 'yellow', 'monkey'], + ['apple', 'red', 'wolf'], + ['melon', 'green', 'aardvark'], + ]; + + // Export to file. + $exportfile = dataformat::write_data('My export', 'ods', $columns, $rows); + + // Read the file. + $odscells = $this->get_ods_rows_content(file_get_contents($exportfile)); + + $this->assertEquals(array_merge([$columns], $rows), $odscells); + } + + /** + * Get ods rows from binary content + * @param string $content + * @return array + * @throws \Box\Spout\Common\Exception\IOException + * @throws \Box\Spout\Reader\Exception\ReaderNotOpenedException + */ + private function get_ods_rows_content($content) { + $reader = \Box\Spout\Reader\Common\Creator\ReaderFactory::createFromType(\Box\Spout\Common\Type::ODS); + $file = tempnam(sys_get_temp_dir(), 'ods_'); + $handle = fopen($file, "w"); + fwrite($handle, $content); + $reader->open($file); + /** @var \Box\Spout\Reader\ODS\Sheet[] $sheets */ + $sheets = $reader->getSheetIterator(); + $rowscellsvalues = []; + foreach ($sheets as $sheet) { + /** @var \Box\Spout\Common\Entity\Row[] $rows */ + $rows = $sheet->getRowIterator(); + foreach ($rows as $row) { + $thisvalues = []; + foreach ($row->getCells() as $cell) { + $thisvalues[] = $cell->getValue(); + } + $rowscellsvalues[] = $thisvalues; + } + } + + return $rowscellsvalues; + } +} diff --git a/lib/spout/readme_moodle.txt b/lib/spout/readme_moodle.txt index 2fab36e584b..b2812a56a4e 100644 --- a/lib/spout/readme_moodle.txt +++ b/lib/spout/readme_moodle.txt @@ -12,6 +12,7 @@ MDL-73624 needs to fix with a couple of minor changes to Writer/WriterAbstract.php. The changes replace rawurldecode() with rawurlencode() in lines 143 and 144. by Meirza +MDL-76494 compatibility for PHP 8.1 2021/09/01 ---------- diff --git a/lib/spout/src/Spout/Common/Helper/GlobalFunctionsHelper.php b/lib/spout/src/Spout/Common/Helper/GlobalFunctionsHelper.php index 5deb8d93f26..08d475dbe90 100644 --- a/lib/spout/src/Spout/Common/Helper/GlobalFunctionsHelper.php +++ b/lib/spout/src/Spout/Common/Helper/GlobalFunctionsHelper.php @@ -243,10 +243,10 @@ class GlobalFunctionsHelper * @see basename() * * @param string $path - * @param string|null $suffix + * @param string $suffix * @return string */ - public function basename($path, $suffix = null) + public function basename($path, $suffix = '') { return \basename($path, $suffix); } diff --git a/lib/spout/src/Spout/Reader/CSV/RowIterator.php b/lib/spout/src/Spout/Reader/CSV/RowIterator.php index 78e66501320..c3f5592858b 100644 --- a/lib/spout/src/Spout/Reader/CSV/RowIterator.php +++ b/lib/spout/src/Spout/Reader/CSV/RowIterator.php @@ -84,6 +84,7 @@ class RowIterator implements IteratorInterface * * @return void */ + #[\ReturnTypeWillChange] public function rewind() { $this->rewindAndSkipBom(); @@ -114,6 +115,7 @@ class RowIterator implements IteratorInterface * * @return bool */ + #[\ReturnTypeWillChange] public function valid() { return ($this->filePointer && !$this->hasReachedEndOfFile); @@ -126,6 +128,7 @@ class RowIterator implements IteratorInterface * @throws \Box\Spout\Common\Exception\EncodingConversionException If unable to convert data to UTF-8 * @return void */ + #[\ReturnTypeWillChange] public function next() { $this->hasReachedEndOfFile = $this->globalFunctionsHelper->feof($this->filePointer); @@ -224,6 +227,7 @@ class RowIterator implements IteratorInterface * * @return Row|null */ + #[\ReturnTypeWillChange] public function current() { return $this->rowBuffer; @@ -235,6 +239,7 @@ class RowIterator implements IteratorInterface * * @return int */ + #[\ReturnTypeWillChange] public function key() { return $this->numReadRows; diff --git a/lib/spout/src/Spout/Reader/CSV/SheetIterator.php b/lib/spout/src/Spout/Reader/CSV/SheetIterator.php index 69eb58a0ba9..27669c77c2e 100644 --- a/lib/spout/src/Spout/Reader/CSV/SheetIterator.php +++ b/lib/spout/src/Spout/Reader/CSV/SheetIterator.php @@ -30,6 +30,7 @@ class SheetIterator implements IteratorInterface * * @return void */ + #[\ReturnTypeWillChange] public function rewind() { $this->hasReadUniqueSheet = false; @@ -41,6 +42,7 @@ class SheetIterator implements IteratorInterface * * @return bool */ + #[\ReturnTypeWillChange] public function valid() { return (!$this->hasReadUniqueSheet); @@ -52,6 +54,7 @@ class SheetIterator implements IteratorInterface * * @return void */ + #[\ReturnTypeWillChange] public function next() { $this->hasReadUniqueSheet = true; @@ -63,6 +66,7 @@ class SheetIterator implements IteratorInterface * * @return \Box\Spout\Reader\CSV\Sheet */ + #[\ReturnTypeWillChange] public function current() { return $this->sheet; @@ -74,6 +78,7 @@ class SheetIterator implements IteratorInterface * * @return int */ + #[\ReturnTypeWillChange] public function key() { return 1; diff --git a/lib/spout/src/Spout/Reader/ODS/RowIterator.php b/lib/spout/src/Spout/Reader/ODS/RowIterator.php index 81f0953d811..27a06001a65 100644 --- a/lib/spout/src/Spout/Reader/ODS/RowIterator.php +++ b/lib/spout/src/Spout/Reader/ODS/RowIterator.php @@ -118,6 +118,7 @@ class RowIterator implements IteratorInterface * @throws \Box\Spout\Reader\Exception\IteratorNotRewindableException If the iterator is rewound more than once * @return void */ + #[\ReturnTypeWillChange] public function rewind() { // Because sheet and row data is located in the file, we can't rewind both the @@ -142,6 +143,7 @@ class RowIterator implements IteratorInterface * * @return bool */ + #[\ReturnTypeWillChange] public function valid() { return (!$this->hasReachedEndOfFile); @@ -155,6 +157,7 @@ class RowIterator implements IteratorInterface * @throws \Box\Spout\Common\Exception\IOException If unable to read the sheet data XML * @return void */ + #[\ReturnTypeWillChange] public function next() { if ($this->doesNeedDataForNextRowToBeProcessed()) { @@ -356,6 +359,7 @@ class RowIterator implements IteratorInterface * * @return Row */ + #[\ReturnTypeWillChange] public function current() { return $this->rowBuffer; @@ -367,6 +371,7 @@ class RowIterator implements IteratorInterface * * @return int */ + #[\ReturnTypeWillChange] public function key() { return $this->lastRowIndexProcessed; @@ -377,6 +382,7 @@ class RowIterator implements IteratorInterface * * @return void */ + #[\ReturnTypeWillChange] public function end() { $this->xmlReader->close(); diff --git a/lib/spout/src/Spout/Reader/ODS/SheetIterator.php b/lib/spout/src/Spout/Reader/ODS/SheetIterator.php index c7b8cd9dcf8..7dd420bc6c2 100644 --- a/lib/spout/src/Spout/Reader/ODS/SheetIterator.php +++ b/lib/spout/src/Spout/Reader/ODS/SheetIterator.php @@ -79,6 +79,7 @@ class SheetIterator implements IteratorInterface * @throws \Box\Spout\Common\Exception\IOException If unable to open the XML file containing sheets' data * @return void */ + #[\ReturnTypeWillChange] public function rewind() { $this->xmlReader->close(); @@ -131,6 +132,7 @@ class SheetIterator implements IteratorInterface * * @return bool */ + #[\ReturnTypeWillChange] public function valid() { return $this->hasFoundSheet; @@ -142,6 +144,7 @@ class SheetIterator implements IteratorInterface * * @return void */ + #[\ReturnTypeWillChange] public function next() { $this->hasFoundSheet = $this->xmlReader->readUntilNodeFound(self::XML_NODE_TABLE); @@ -157,6 +160,7 @@ class SheetIterator implements IteratorInterface * * @return \Box\Spout\Reader\ODS\Sheet */ + #[\ReturnTypeWillChange] public function current() { $escapedSheetName = $this->xmlReader->getAttribute(self::XML_ATTRIBUTE_TABLE_NAME); @@ -214,6 +218,7 @@ class SheetIterator implements IteratorInterface * * @return int */ + #[\ReturnTypeWillChange] public function key() { return $this->currentSheetIndex + 1; @@ -224,6 +229,7 @@ class SheetIterator implements IteratorInterface * * @return void */ + #[\ReturnTypeWillChange] public function end() { $this->xmlReader->close(); diff --git a/lib/spout/src/Spout/Reader/Wrapper/XMLReader.php b/lib/spout/src/Spout/Reader/Wrapper/XMLReader.php index 6f85f320c8f..a05b72d85ed 100644 --- a/lib/spout/src/Spout/Reader/Wrapper/XMLReader.php +++ b/lib/spout/src/Spout/Reader/Wrapper/XMLReader.php @@ -83,6 +83,7 @@ class XMLReader extends \XMLReader * @throws \Box\Spout\Reader\Exception\XMLProcessingException If an error/warning occurred * @return bool TRUE on success or FALSE on failure */ + #[\ReturnTypeWillChange] public function read() { $this->useXMLInternalErrors(); @@ -119,6 +120,7 @@ class XMLReader extends \XMLReader * @throws \Box\Spout\Reader\Exception\XMLProcessingException If an error/warning occurred * @return bool TRUE on success or FALSE on failure */ + #[\ReturnTypeWillChange] public function next($localName = null) { $this->useXMLInternalErrors(); diff --git a/lib/spout/src/Spout/Reader/XLSX/RowIterator.php b/lib/spout/src/Spout/Reader/XLSX/RowIterator.php index a54b8b192b0..9c891a0415d 100644 --- a/lib/spout/src/Spout/Reader/XLSX/RowIterator.php +++ b/lib/spout/src/Spout/Reader/XLSX/RowIterator.php @@ -139,6 +139,7 @@ class RowIterator implements IteratorInterface * @throws \Box\Spout\Common\Exception\IOException If the sheet data XML cannot be read * @return void */ + #[\ReturnTypeWillChange] public function rewind() { $this->xmlReader->close(); @@ -163,6 +164,7 @@ class RowIterator implements IteratorInterface * * @return bool */ + #[\ReturnTypeWillChange] public function valid() { return (!$this->hasReachedEndOfFile); @@ -176,6 +178,7 @@ class RowIterator implements IteratorInterface * @throws \Box\Spout\Common\Exception\IOException If unable to read the sheet data XML * @return void */ + #[\ReturnTypeWillChange] public function next() { $this->nextRowIndexToBeProcessed++; @@ -374,6 +377,7 @@ class RowIterator implements IteratorInterface * * @return Row|null */ + #[\ReturnTypeWillChange] public function current() { $rowToBeProcessed = $this->rowBuffer; @@ -399,6 +403,7 @@ class RowIterator implements IteratorInterface * * @return int */ + #[\ReturnTypeWillChange] public function key() { // TODO: This should return $this->nextRowIndexToBeProcessed @@ -414,6 +419,7 @@ class RowIterator implements IteratorInterface * * @return void */ + #[\ReturnTypeWillChange] public function end() { $this->xmlReader->close(); diff --git a/lib/spout/src/Spout/Reader/XLSX/SheetIterator.php b/lib/spout/src/Spout/Reader/XLSX/SheetIterator.php index 81f481c3cdc..aae8897eef4 100644 --- a/lib/spout/src/Spout/Reader/XLSX/SheetIterator.php +++ b/lib/spout/src/Spout/Reader/XLSX/SheetIterator.php @@ -38,6 +38,7 @@ class SheetIterator implements IteratorInterface * * @return void */ + #[\ReturnTypeWillChange] public function rewind() { $this->currentSheetIndex = 0; @@ -49,6 +50,7 @@ class SheetIterator implements IteratorInterface * * @return bool */ + #[\ReturnTypeWillChange] public function valid() { return ($this->currentSheetIndex < \count($this->sheets)); @@ -60,6 +62,7 @@ class SheetIterator implements IteratorInterface * * @return void */ + #[\ReturnTypeWillChange] public function next() { // Using isset here because it is way faster than array_key_exists... @@ -77,6 +80,7 @@ class SheetIterator implements IteratorInterface * * @return \Box\Spout\Reader\XLSX\Sheet */ + #[\ReturnTypeWillChange] public function current() { return $this->sheets[$this->currentSheetIndex]; @@ -88,6 +92,7 @@ class SheetIterator implements IteratorInterface * * @return int */ + #[\ReturnTypeWillChange] public function key() { return $this->currentSheetIndex + 1; @@ -98,6 +103,7 @@ class SheetIterator implements IteratorInterface * * @return void */ + #[\ReturnTypeWillChange] public function end() { // make sure we are not leaking memory in case the iteration stopped before the end diff --git a/reportbuilder/tests/behat/customreports.feature b/reportbuilder/tests/behat/customreports.feature index 0be0833214f..d25196b7315 100644 --- a/reportbuilder/tests/behat/customreports.feature +++ b/reportbuilder/tests/behat/customreports.feature @@ -252,3 +252,24 @@ Feature: Manage custom reports And I click on "Close 'Report users' editor" "button" And I press "View" action in the "Report users" report row And I should see "admin" in the "reportbuilder-table" "table" + + Scenario Outline: Download custom report in different formats + Given the following "users" exist: + | username | firstname | lastname | + | user1 | User | 1 | + | user2 | User | 2 | + And the following "core_reportbuilder > Reports" exist: + | name | source | + | Report users | core_user\reportbuilder\datasource\users | + When I am on the "Report users" "reportbuilder > Editor" page logged in as "admin" + And I click on "Switch to preview mode" "button" + Then I set the field "Download table data as" to "" + And I press "Download" + Examples: + | format | + | Comma separated values (.csv) | + | Microsoft Excel (.xlsx) | + | HTML table | + | Javascript Object Notation (.json) | + | OpenDocument (.ods) | + | Portable Document Format (.pdf) |