diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c1a549c6c..aac3a32de8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Implemented URLENCODE() Web Function - Implemented the CHITEST(), CHISQ.DIST() and CHISQ.INV() and equivalent Statistical functions, for both left- and right-tailed distributions. - Support for ActiveSheet and SelectedCells in the ODS Reader and Writer. [PR #1908](https://github.com/PHPOffice/PhpSpreadsheet/pull/1908) +- Support for notContainsText Conditional Style in xlsx [Issue #984](https://github.com/PHPOffice/PhpSpreadsheet/issues/984) ### Changed diff --git a/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php b/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php index 5f6cb4dc35..dcd7ad12cb 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php +++ b/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php @@ -2,7 +2,6 @@ namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx; -use PhpOffice\PhpSpreadsheet\Style\Color; use PhpOffice\PhpSpreadsheet\Style\Conditional; use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\ConditionalDataBar; use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\ConditionalFormattingRuleExtension; @@ -39,15 +38,7 @@ private function readConditionalStyles($xmlSheet) $conditionals = []; foreach ($xmlSheet->conditionalFormatting as $conditional) { foreach ($conditional->cfRule as $cfRule) { - if ( - ((string) $cfRule['type'] == Conditional::CONDITION_NONE - || (string) $cfRule['type'] == Conditional::CONDITION_CELLIS - || (string) $cfRule['type'] == Conditional::CONDITION_CONTAINSTEXT - || (string) $cfRule['type'] == Conditional::CONDITION_CONTAINSBLANKS - || (string) $cfRule['type'] == Conditional::CONDITION_NOTCONTAINSBLANKS - || (string) $cfRule['type'] == Conditional::CONDITION_EXPRESSION) - && isset($this->dxfs[(int) ($cfRule['dxfId'])]) - ) { + if (Conditional::isValidConditionType((string) $cfRule['type']) && isset($this->dxfs[(int) ($cfRule['dxfId'])])) { $conditionals[(string) $conditional['sqref']][(int) ($cfRule['priority'])] = $cfRule; } elseif ((string) $cfRule['type'] == Conditional::CONDITION_DATABAR) { $conditionals[(string) $conditional['sqref']][(int) ($cfRule['priority'])] = $cfRule; diff --git a/src/PhpSpreadsheet/Style/Conditional.php b/src/PhpSpreadsheet/Style/Conditional.php index b008c9f28b..4cbc2746af 100644 --- a/src/PhpSpreadsheet/Style/Conditional.php +++ b/src/PhpSpreadsheet/Style/Conditional.php @@ -15,6 +15,18 @@ class Conditional implements IComparable const CONDITION_CONTAINSBLANKS = 'containsBlanks'; const CONDITION_NOTCONTAINSBLANKS = 'notContainsBlanks'; const CONDITION_DATABAR = 'dataBar'; + const CONDITION_NOTCONTAINSTEXT = 'notContainsText'; + + private const CONDITION_TYPES = [ + self::CONDITION_CELLIS, + self::CONDITION_CONTAINSBLANKS, + self::CONDITION_CONTAINSTEXT, + self::CONDITION_DATABAR, + self::CONDITION_EXPRESSION, + self::CONDITION_NONE, + self::CONDITION_NOTCONTAINSBLANKS, + self::CONDITION_NOTCONTAINSTEXT, + ]; // Operator types const OPERATOR_NONE = ''; @@ -300,4 +312,12 @@ public function __clone() } } } + + /** + * Verify if param is valid condition type. + */ + public static function isValidConditionType(string $type): bool + { + return in_array($type, self::CONDITION_TYPES); + } } diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php index 9b1d67307f..38dead4f0d 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php @@ -634,15 +634,21 @@ private function writeConditionalFormatting(XMLWriter $objWriter, Phpspreadsheet self::writeAttributeif( $objWriter, - ($conditional->getConditionType() == Conditional::CONDITION_CELLIS || $conditional->getConditionType() == Conditional::CONDITION_CONTAINSTEXT) - && $conditional->getOperatorType() != Conditional::OPERATOR_NONE, + ( + $conditional->getConditionType() === Conditional::CONDITION_CELLIS + || $conditional->getConditionType() === Conditional::CONDITION_CONTAINSTEXT + || $conditional->getConditionType() === Conditional::CONDITION_NOTCONTAINSTEXT + ) && $conditional->getOperatorType() !== Conditional::OPERATOR_NONE, 'operator', $conditional->getOperatorType() ); self::writeAttributeIf($objWriter, $conditional->getStopIfTrue(), 'stopIfTrue', '1'); - if ($conditional->getConditionType() == Conditional::CONDITION_CONTAINSTEXT) { + if ( + $conditional->getConditionType() === Conditional::CONDITION_CONTAINSTEXT + || $conditional->getConditionType() === Conditional::CONDITION_NOTCONTAINSTEXT + ) { self::writeTextCondElements($objWriter, $conditional, $cellCoordinate); } else { self::writeOtherCondElements($objWriter, $conditional, $cellCoordinate); diff --git a/tests/PhpSpreadsheetTests/Reader/Xlsx/ConditionalTest.php b/tests/PhpSpreadsheetTests/Reader/Xlsx/ConditionalTest.php new file mode 100644 index 0000000000..8108d988ad --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/Xlsx/ConditionalTest.php @@ -0,0 +1,30 @@ +load($filename); + $worksheet = $spreadsheet->getActiveSheet(); + $styles = $worksheet->getConditionalStyles('A1:A5'); + + self::assertCount(1, $styles); + + /** @var Conditional $notContainsTextStyle */ + $notContainsTextStyle = $styles[0]; + self::assertEquals('A', $notContainsTextStyle->getText()); + self::assertEquals(Conditional::CONDITION_NOTCONTAINSTEXT, $notContainsTextStyle->getConditionType()); + self::assertEquals(Conditional::OPERATOR_NOTCONTAINS, $notContainsTextStyle->getOperatorType()); + } +} diff --git a/tests/PhpSpreadsheetTests/Writer/Xlsx/ConditionalTest.php b/tests/PhpSpreadsheetTests/Writer/Xlsx/ConditionalTest.php new file mode 100644 index 0000000000..21df6ed6ee --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Xlsx/ConditionalTest.php @@ -0,0 +1,60 @@ +prevValue = Settings::getLibXmlLoaderOptions(); + + // Disable validating XML with the DTD + Settings::setLibXmlLoaderOptions($this->prevValue & ~LIBXML_DTDVALID & ~LIBXML_DTDATTR & ~LIBXML_DTDLOAD); + } + + protected function tearDown(): void + { + Settings::setLibXmlLoaderOptions($this->prevValue); + } + + /** + * Test check if conditional style with type 'notContainsText' works on xlsx. + */ + public function testConditionalNotContainsText(): void + { + $spreadsheet = new Spreadsheet(); + $worksheet = $spreadsheet->getActiveSheet(); + + $condition = new Conditional(); + $condition->setConditionType(Conditional::CONDITION_NOTCONTAINSTEXT); + $condition->setOperatorType(Conditional::OPERATOR_NOTCONTAINS); + $condition->setText('C'); + $condition->getStyle()->applyFromArray([ + 'fill' => [ + 'color' => ['argb' => 'FFFFC000'], + 'fillType' => Fill::FILL_SOLID, + ], + ]); + $worksheet->setConditionalStyles('A1:A5', [$condition]); + + $writer = new Xlsx($spreadsheet); + $writerWorksheet = new Xlsx\Worksheet($writer); + $data = $writerWorksheet->writeWorksheet($worksheet, []); + $needle = <<ISERROR(SEARCH("C",A1:A5)) +xml; + self::assertStringContainsString($needle, $data); + } +} diff --git a/tests/data/Reader/XLSX/conditionalFormatting3Test.xlsx b/tests/data/Reader/XLSX/conditionalFormatting3Test.xlsx new file mode 100644 index 0000000000..ee23e21c8c Binary files /dev/null and b/tests/data/Reader/XLSX/conditionalFormatting3Test.xlsx differ