Skip to content

Commit

Permalink
Support Data Validations in More Versions of Excel
Browse files Browse the repository at this point in the history
Attempt to deal with PHPOffice#2368, this time for good. Some deleted code was accidentally restored just before release 19, causing errors in spreadsheets with Data Validations. PR PHPOffice#2369 removed the duplicated code, and the fix was confirmed in current versions of Excel for Windows, Google sheets, and other versions of Excel. However, there were problems reported in earlier version of Excel for Windows, and some, versions of Excel for Mac, not all but including a recent one. This change, which is simpler than the original (no need for extLst) fix for DataValidations, is tested with Excel 2007 and Excel 2003 as well as more recent versions. I do not have a Mac on which to test.
  • Loading branch information
oleibman committed Nov 6, 2021
1 parent ca5bd9b commit 1f05ce0
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 46 deletions.
68 changes: 22 additions & 46 deletions src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use PhpOffice\PhpSpreadsheet\Cell\Cell;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Cell\DataValidation;
use PhpOffice\PhpSpreadsheet\RichText\RichText;
use PhpOffice\PhpSpreadsheet\Settings;
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
Expand Down Expand Up @@ -121,8 +122,6 @@ public function writeWorksheet(PhpspreadsheetWorksheet $worksheet, $pStringTable
// ConditionalFormattingRuleExtensionList
// (Must be inserted last. Not insert last, an Excel parse error will occur)
$this->writeExtLst($objWriter, $worksheet);
// dataValidations
$this->writeDataValidations($objWriter, $worksheet);

$objWriter->endElement();

Expand Down Expand Up @@ -473,6 +472,13 @@ private static function writeAttributeNotNull(XMLWriter $objWriter, string $attr
}
}

private static function writeAttributeNotNullString(XMLWriter $objWriter, string $attr, string $val): void
{
if ($val !== '') {
$objWriter->writeAttribute($attr, $val);
}
}

private static function writeElementIf(XMLWriter $objWriter, $condition, string $attr, string $val): void
{
if ($condition) {
Expand Down Expand Up @@ -673,8 +679,6 @@ private function writeConditionalFormatting(XMLWriter $objWriter, Phpspreadsheet

/**
* Write DataValidations.
*
* @param XMLWriter $objWriter XML Writer
*/
private function writeDataValidations(XMLWriter $objWriter, PhpspreadsheetWorksheet $worksheet): void
{
Expand All @@ -684,66 +688,38 @@ private function writeDataValidations(XMLWriter $objWriter, PhpspreadsheetWorksh
// Write data validations?
if (!empty($dataValidationCollection)) {
$dataValidationCollection = Coordinate::mergeRangesInCollection($dataValidationCollection);
$objWriter->startElement('extLst');
$objWriter->startElement('ext');
$objWriter->writeAttribute('uri', '{CCE6A557-97BC-4b89-ADB6-D9C93CAAB3DF}');
$objWriter->writeAttribute('xmlns:x14', 'http://schemas.microsoft.com/office/spreadsheetml/2009/9/main');
$objWriter->startElement('x14:dataValidations');
$objWriter->startElement('dataValidations');
$objWriter->writeAttribute('count', count($dataValidationCollection));
$objWriter->writeAttribute('xmlns:xm', 'http://schemas.microsoft.com/office/excel/2006/main');

foreach ($dataValidationCollection as $coordinate => $dv) {
$objWriter->startElement('x14:dataValidation');

if ($dv->getType() != '') {
$objWriter->writeAttribute('type', $dv->getType());
}

if ($dv->getErrorStyle() != '') {
$objWriter->writeAttribute('errorStyle', $dv->getErrorStyle());
}

if ($dv->getOperator() != '') {
$objWriter->writeAttribute('operator', $dv->getOperator());
}
$objWriter->startElement('dataValidation');

self::writeAttributeNotNullString($objWriter, 'type', $dv->getType());
self::writeAttributeNotNullString($objWriter, 'errorStyle', $dv->getErrorStyle());
self::writeAttributeNotNullString($objWriter, 'operator', $dv->getOperator());
$objWriter->writeAttribute('allowBlank', ($dv->getAllowBlank() ? '1' : '0'));
// showDropDown is really hideDropDown Excel renders as true = hide, false = show
$objWriter->writeAttribute('showDropDown', (!$dv->getShowDropDown() ? '1' : '0'));
$objWriter->writeAttribute('showInputMessage', ($dv->getShowInputMessage() ? '1' : '0'));
$objWriter->writeAttribute('showErrorMessage', ($dv->getShowErrorMessage() ? '1' : '0'));

if ($dv->getErrorTitle() !== '') {
$objWriter->writeAttribute('errorTitle', $dv->getErrorTitle());
}
if ($dv->getError() !== '') {
$objWriter->writeAttribute('error', $dv->getError());
}
if ($dv->getPromptTitle() !== '') {
$objWriter->writeAttribute('promptTitle', $dv->getPromptTitle());
}
if ($dv->getPrompt() !== '') {
$objWriter->writeAttribute('prompt', $dv->getPrompt());
}
self::writeAttributeNotNullString($objWriter, 'errorTitle', $dv->getErrorTitle());
self::writeAttributeNotNullString($objWriter, 'error', $dv->getError());
self::writeAttributeNotNullString($objWriter, 'promptTitle', $dv->getPromptTitle());
self::writeAttributeNotNullString($objWriter, 'prompt', $dv->getPrompt());

$objWriter->writeAttribute('sqref', $dv->getSqref() ?? $coordinate);

if ($dv->getFormula1() !== '') {
$objWriter->startElement('x14:formula1');
$objWriter->writeElement('xm:f', $dv->getFormula1());
$objWriter->endElement();
$objWriter->writeElement('formula1', $dv->getFormula1());
}
if ($dv->getFormula2() !== '') {
$objWriter->startElement('x14:formula2');
$objWriter->writeElement('xm:f', $dv->getFormula2());
$objWriter->endElement();
$objWriter->writeElement('formula2', $dv->getFormula2());
}
$objWriter->writeElement('xm:sqref', $dv->getSqref() ?? $coordinate);

$objWriter->endElement();
}

$objWriter->endElement(); // dataValidations
$objWriter->endElement(); // ext
$objWriter->endElement(); // extLst
$objWriter->endElement();
}
}

Expand Down
37 changes: 37 additions & 0 deletions tests/PhpSpreadsheetTests/Writer/Xlsx/Issue2368Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace PhpOffice\PhpSpreadsheetTests\Writer\Xlsx;

use PhpOffice\PhpSpreadsheet\Cell\DataValidation;
use PhpOffice\PhpSpreadsheet\Shared\File;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx as Writer;
use PHPUnit\Framework\TestCase;

class Issue2368Test extends TestCase
{
public function testBoolWrite(): void
{
// DataValidations were incorrectly written twice.
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$validation = $sheet->getDataValidation('A1:A10');
$validation->setType(DataValidation::TYPE_LIST);
$validation->setShowDropDown(true);
$validation->setFormula1('"Option 1, Option 2"');

$outputFilename = File::temporaryFilename();
$writer = new Writer($spreadsheet);
$writer->save($outputFilename);
$zipfile = "zip://$outputFilename#xl/worksheets/sheet1.xml";
$contents = file_get_contents($zipfile);
unlink($outputFilename);
$spreadsheet->disconnectWorksheets();
if ($contents === false) {
self::fail('Unable to open file');
} else {
self::assertSame(0, substr_count($contents, '<extLst>'));
self::assertSame(2, substr_count($contents, 'dataValidations')); // start and end tags
}
}
}

0 comments on commit 1f05ce0

Please sign in to comment.