From 83d1c981c5fff2b9918ed890a34b1d77eb8b9def Mon Sep 17 00:00:00 2001 From: Luqgreg Date: Sun, 26 Aug 2018 13:00:00 +0200 Subject: [PATCH] Add support for more color formats (#12) --- .../PortableInfoboxParserTagController.php | 4 +- .../Helpers/InfoboxParamsValidator.php | 195 +++++++++++++++++- tests/phpunit/InfoboxParamsValidatorTest.php | 101 +++++++-- 3 files changed, 268 insertions(+), 32 deletions(-) diff --git a/includes/controllers/PortableInfoboxParserTagController.php b/includes/controllers/PortableInfoboxParserTagController.php index adc2c4ab..f546ef5f 100644 --- a/includes/controllers/PortableInfoboxParserTagController.php +++ b/includes/controllers/PortableInfoboxParserTagController.php @@ -208,9 +208,7 @@ private function getColor( $colorParam, $params, PPFrame $frame ) { } private function sanitizeColor( $color ) { - $color = substr( $color, 0, 1 ) === '#' ? $color : '#' . $color; - $color = ( $this->getParamsValidator()->validateColorValue( $color ) ) ? $color : ''; - return $color; + return $this->getParamsValidator()->validateColorValue( $color ); } private function getParamsValidator() { diff --git a/includes/services/Helpers/InfoboxParamsValidator.php b/includes/services/Helpers/InfoboxParamsValidator.php index aae54d08..87091090 100644 --- a/includes/services/Helpers/InfoboxParamsValidator.php +++ b/includes/services/Helpers/InfoboxParamsValidator.php @@ -2,7 +2,7 @@ namespace PortableInfobox\Helpers; class InfoboxParamsValidator { - private $supportedParams = [ + private static $supportedParams = [ 'accent-color-default', 'accent-color-source', 'accent-color-text-default', @@ -12,11 +12,178 @@ class InfoboxParamsValidator { 'theme-source', ]; - private $supportedLayouts = [ + private static $supportedLayouts = [ 'default', 'stacked' ]; + private static $colorNames = [ + 'aliceblue', + 'antiquewhite', + 'aqua', + 'aquamarine', + 'azure', + 'beige', + 'bisque', + 'black', + 'blanchedalmond', + 'blue', + 'blueviolet', + 'brown', + 'burlywood', + 'cadetblue', + 'chartreuse', + 'chocolate', + 'coral', + 'cornflowerblue', + 'cornsilk', + 'crimson', + 'cyan', + 'darkblue', + 'darkcyan', + 'darkgoldenrod', + 'darkgray', + 'darkgrey', + 'darkgreen', + 'darkkhaki', + 'darkmagenta', + 'darkolivegreen', + 'darkorange', + 'darkorchid', + 'darkred', + 'darksalmon', + 'darkseagreen', + 'darkslateblue', + 'darkslategray', + 'darkslategrey', + 'darkturquoise', + 'darkviolet', + 'deeppink', + 'deepskyblue', + 'dimgray', + 'dimgrey', + 'dodgerblue', + 'firebrick', + 'floralwhite', + 'forestgreen', + 'fuchsia', + 'gainsboro', + 'ghostwhite', + 'gold', + 'goldenrod', + 'gray', + 'grey', + 'green', + 'greenyellow', + 'honeydew', + 'hotpink', + 'indianred', + 'indigo', + 'ivory', + 'khaki', + 'lavender', + 'lavenderblush', + 'lawngreen', + 'lemonchiffon', + 'lightblue', + 'lightcoral', + 'lightcyan', + 'lightgoldenrodyellow', + 'lightgray', + 'lightgrey', + 'lightgreen', + 'lightpink', + 'lightsalmon', + 'lightseagreen', + 'lightskyblue', + 'lightslategray', + 'lightslategrey', + 'lightsteelblue', + 'lightyellow', + 'lime', + 'limegreen', + 'linen', + 'magenta', + 'maroon', + 'mediumaquamarine', + 'mediumblue', + 'mediumorchid', + 'mediumpurple', + 'mediumseagreen', + 'mediumslateblue', + 'mediumspringgreen', + 'mediumturquoise', + 'mediumvioletred', + 'midnightblue', + 'mintcream', + 'mistyrose', + 'moccasin', + 'navajowhite', + 'navy', + 'oldlace', + 'olive', + 'olivedrab', + 'orange', + 'orangered', + 'orchid', + 'palegoldenrod', + 'palegreen', + 'paleturquoise', + 'palevioletred', + 'papayawhip', + 'peachpuff', + 'peru', + 'pink', + 'plum', + 'powderblue', + 'purple', + 'red', + 'rosybrown', + 'royalblue', + 'saddlebrown', + 'salmon', + 'sandybrown', + 'seagreen', + 'seashell', + 'sienna', + 'silver', + 'skyblue', + 'slateblue', + 'slategray', + 'slategrey', + 'snow', + 'springgreen', + 'steelblue', + 'tan', + 'teal', + 'thistle', + 'tomato', + 'turquoise', + 'violet', + 'wheat', + 'white', + 'whitesmoke', + 'yellow', + 'yellowgreen' + ]; + private static $colorNamesFlipped; + + const REGEX_PERCENT = '(?:100|\d{1,2})%'; + const REGEX_HUE = '-?(?:3(?:60|[0-5]\d)|[12]?\d{1,2})'; + const REGEX_ALPHAVAL = '(?:' . self::REGEX_PERCENT . '|[01]?\.\d+|[01])'; + const REGEX_RGBVAL = '(?:' . self::REGEX_PERCENT . '|2(?:5[0-5]|[0-4]\d)|1?\d{1,2})'; + const REGEX_HEXRGB = '/^#?[a-f0-9]{3}(?:[a-f0-9]{3}(?:[a-f0-9]{2})?|[a-f0-9])?$/'; + const REGEX_RGB = '/^rgb\((?:' . self::REGEX_RGBVAL . ',){2}' . self::REGEX_RGBVAL . '\)$/'; + const REGEX_RGBA = '/^rgba\((?:' . self::REGEX_RGBVAL . ',){3}' . self::REGEX_ALPHAVAL . '\)$/'; + const REGEX_HSL = '/^hsl\(' . self::REGEX_HUE . ',' . self::REGEX_PERCENT . ',' . self::REGEX_PERCENT . '\)$/'; + const REGEX_HSLA = '/^hsla\(' . self::REGEX_HUE . ',(?:' . self::REGEX_PERCENT . ',){2}' . self::REGEX_ALPHAVAL . '\)$/'; + + public function __construct() { + if( is_null( self::$colorNamesFlipped ) ) { + self::$colorNamesFlipped = array_flip( self::$colorNames ); + } + } + /** * validates infobox tags attribute names * @param array $params @@ -26,7 +193,7 @@ class InfoboxParamsValidator { */ public function validateParams( $params ) { foreach ( array_keys( $params ) as $param ) { - if ( !in_array( $param, $this->supportedParams ) ) { + if ( !in_array( $param, self::$supportedParams ) ) { throw new InvalidInfoboxParamsException( $param ); } } @@ -35,13 +202,27 @@ public function validateParams( $params ) { } /** - * validates if argument is valid color value. Currently only hex values are supported + * validates if argument is valid color value. * @param string $color * @return bool - * @throws InvalidColorValueException */ public function validateColorValue( $color ) { - return !empty( preg_match( '/^(#[a-f0-9]{3}([a-f0-9]{3})?)$/i', $color ) ); + if ( preg_match( self::REGEX_HEXRGB, $color ) ) { + return substr( $color, 0, 1 ) === '#' ? $color : '#' . $color; + } + + $color = strtolower( preg_replace( '/\s+/', '', $color ) ); + + if ( isset( self::$colorNamesFlipped[$color] ) || + preg_match( self::REGEX_RGB, $color ) || + preg_match( self::REGEX_RGBA, $color ) || + preg_match( self::REGEX_HSL, $color ) || + preg_match( self::REGEX_HSLA, $color ) + ) { + return $color; + } + + return ''; } /** @@ -50,7 +231,7 @@ public function validateColorValue( $color ) { * @return bool */ public function validateLayout( $layoutName ) { - return $layoutName && in_array( $layoutName, $this->supportedLayouts ); + return $layoutName && in_array( $layoutName, self::$supportedLayouts ); } } diff --git a/tests/phpunit/InfoboxParamsValidatorTest.php b/tests/phpunit/InfoboxParamsValidatorTest.php index 84d4c462..4160791e 100644 --- a/tests/phpunit/InfoboxParamsValidatorTest.php +++ b/tests/phpunit/InfoboxParamsValidatorTest.php @@ -73,11 +73,15 @@ public function infoboxParamsPassValidationDataProvider() { } /** - * @param array $color + * @param string $color + * @param bool $addHash * @dataProvider passValidateColorValueDataProvider */ - public function testPassValidateColorValue( $color ) { - $this->assertTrue( $this->InfoboxParamsValidator->validateColorValue( $color ) ); + public function testPassValidateColorValue( $color, $addHash = false ) { + $this->assertEquals( + ( $addHash ? '#' : '' ) . strtolower( preg_replace( '/\s+/', '', $color ) ), + $this->InfoboxParamsValidator->validateColorValue( $color ) + ); } public function passValidateColorValueDataProvider() { @@ -89,6 +93,7 @@ public function passValidateColorValueDataProvider() { [ 'color' => '#fff' ], [ 'color' => '#000' ], [ 'color' => '#999' ], + [ 'color' => '#aaaa' ], [ 'color' => '#aaaaaa' ], [ 'color' => '#abcabc' ], [ 'color' => '#a12acd' ], @@ -98,6 +103,53 @@ public function passValidateColorValueDataProvider() { [ 'color' => '#ffffff' ], [ 'color' => '#000000' ], [ 'color' => '#999999' ], + [ 'color' => '#ffffffff' ], + [ 'color' => 'aaa', true ], + [ 'color' => 'abc', true ], + [ 'color' => 'a12', true ], + [ 'color' => '12f', true ], + [ 'color' => 'fff', true ], + [ 'color' => '000', true ], + [ 'color' => '999', true ], + [ 'color' => 'aaaa', true ], + [ 'color' => 'a12acd', true ], + [ 'color' => 'aaaaaa', true ], + [ 'color' => 'abcabc', true ], + [ 'color' => 'ffffff', true ], + [ 'color' => '000000', true ], + [ 'color' => '999999', true ], + [ 'color' => '125fff', true ], + [ 'color' => 'ffffffff', true ], + [ 'color' => 'rgb(0,0,0)' ], + [ 'color' => 'RGB(20,3,255)' ], + [ 'color' => 'rgb( 20, 3, 255 )' ], + [ 'color' => 'rgb(255,255,255)' ], + [ 'color' => 'rgb(0%,25%,100%)' ], + [ 'color' => 'rgb(10%,75%,33%)' ], + [ 'color' => 'RGB(100%,100%,100%)' ], + [ 'color' => 'rgba(0,0,0,0)' ], + [ 'color' => 'rgba(0,0,0,0%)' ], + [ 'color' => 'rgba(20,3,255,50%)' ], + [ 'color' => 'RGBA(20, 3, 255,.5)' ], + [ 'color' => 'rgba( 20, 3, 255, 0.5 )' ], + [ 'color' => 'rgba(255,255,255, 1)' ], + [ 'color' => 'rgba(255,255,255, 100%)' ], + [ 'color' => 'rgba(0%,25%,99% ,25%)' ], + [ 'color' => 'RGBA(100%,100%,100%, 1)' ], + [ 'color' => 'rgba(100%,100%,100%, 100%)' ], + [ 'color' => 'hsl(0,0%,0%)' ], + [ 'color' => 'hsl(40, 78%, 45%)' ], + [ 'color' => 'HSL( -80, 58%, 30%)' ], + [ 'color' => 'hsl(360,100%,100%)' ], + [ 'color' => 'hsla(0,0%,0%,0)' ], + [ 'color' => 'hsla(0,0%,0%,0%)' ], + [ 'color' => 'HSLA(40,78%,45%,.3)' ], + [ 'color' => 'hsla( 359, 38%,20% ,0.7)' ], + [ 'color' => 'HSLA(360,100%,100%,100%)' ], + [ 'color' => 'hsla( 360 , 100% , 100%,1)' ], + [ 'color' => 'white' ], + [ 'color' => 'White' ], + [ 'color' => 'WHITE' ], ]; } @@ -106,46 +158,51 @@ public function passValidateColorValueDataProvider() { * @dataProvider failValidateColorValueDataProvider */ public function testFailValidateColorValue( $color ) { - $this->assertFalse( $this->InfoboxParamsValidator->validateColorValue( $color ) ); + $this->assertEquals( '', $this->InfoboxParamsValidator->validateColorValue( $color ) ); } public function failValidateColorValueDataProvider() { return [ [ 'color' => '' ], - [ 'color' => 'aaa' ], - [ 'color' => 'abc' ], - [ 'color' => 'a12' ], - [ 'color' => '12f' ], - [ 'color' => 'fff' ], - [ 'color' => '000' ], - [ 'color' => '999' ], [ 'color' => 'ggg' ], [ 'color' => 'asd' ], [ 'color' => '12g' ], [ 'color' => '1k2' ], [ 'color' => 'l34' ], - [ 'color' => 'aaaa' ], [ 'color' => 'aaag' ], [ 'color' => '#ggg' ], [ 'color' => '#asd' ], [ 'color' => '#12g' ], [ 'color' => '#1k2' ], [ 'color' => '#l34' ], - [ 'color' => '#aaaa' ], [ 'color' => '#aaag' ], [ 'color' => 'aaaaa' ], - [ 'color' => 'aaaaaa' ], - [ 'color' => 'abcabc' ], - [ 'color' => 'a12acd' ], - [ 'color' => '12f126' ], - [ 'color' => 'adf129' ], - [ 'color' => '125fff' ], - [ 'color' => 'ffffff' ], - [ 'color' => '000000' ], - [ 'color' => '999999' ], + [ 'color' => '12fl26' ], + [ 'color' => 'adfl29' ], [ 'color' => 'aaaaaaa' ], [ 'color' => '#aaaaaaa' ], [ 'color' => '#aaaaa' ], + [ 'color' => 'fffffffff' ], + [ 'color' => '#fffffffff' ], + [ 'color' => 'rgb(0a,0a,0a)' ], + [ 'color' => 'rgb(20,3,265)' ], + [ 'color' => 'rgb( -20, 3, 255 )' ], + [ 'color' => 'rgb(256,355,67)' ], + [ 'color' => 'rgb(256,355)' ], + [ 'color' => 'rgb(256,355,)' ], + [ 'color' => 'rgb(256,355,,63)' ], + [ 'color' => 'rgb(552,355,,63)' ], + [ 'color' => 'rgb(0%,25%,102%)' ], + [ 'color' => 'rgba(255,255,255,1.)' ], + [ 'color' => 'rgba(100%,100%,100%)' ], + [ 'color' => 'rgba(100%,100%,100%,.)' ], + [ 'color' => 'hsl(0,0%,0%,0)' ], + [ 'color' => 'hsl(0,-78%,45%)' ], + [ 'color' => 'hsl(0%,85%,12%)' ], + [ 'color' => 'hsl(356,14%,100)' ], + [ 'color' => 'hsla(0,0%,0%)' ], + [ 'color' => 'hsla(0,0%,0%,.)' ], + [ 'color' => 'hsla(0,0%,0%,0.)' ], ]; }