diff --git a/includes/TemplateParser.php b/includes/TemplateParser.php index 78f54c2d4..bcebd5049 100644 --- a/includes/TemplateParser.php +++ b/includes/TemplateParser.php @@ -7,14 +7,15 @@ * * @param string $template * @param array $objects + * @param callable $sanitizeFunction The callable used to remove unwanted tags/characters (use default 'commonsbooking_sanitizeHTML' or 'sanitize_text_field') * * @return mixed */ -function commonsbooking_parse_template( string $template = '', $objects = [] ) { +function commonsbooking_parse_template( string $template = '', $objects = [], $sanitizeFunction = 'commonsbooking_sanitizeHTML' ) { $template = preg_replace_callback( '/\{{.*?\}}/', - function ( $match ) use ( $objects ) { - return commonsbooking_parse_template_callback( $match, $objects ); + function ( $match ) use ( $objects, $sanitizeFunction ) { + return commonsbooking_parse_template_callback( $match, $objects, $sanitizeFunction ); }, $template ); @@ -24,7 +25,7 @@ function ( $match ) use ( $objects ) { if ( preg_match_all( '/{{.*?}}/', $template ) === 0 ) { return apply_filters( 'commonsbooking_template_tag', $template ); } else { - return commonsbooking_parse_template( $template, $objects ); + return commonsbooking_parse_template( $template, $objects, $sanitizeFunction ); } } @@ -41,24 +42,27 @@ function commonsbooking_parse_shortcode( $tag ) { * * @param mixed $match * @param array $objects + * @param callable $sanitizeFunction The callable used to remove unwanted tags/characters * * @return false|mixed */ -function commonsbooking_parse_template_callback( $match, array $objects = [] ) { +function commonsbooking_parse_template_callback( $match, array $objects = [], $sanitizeFunction = 'commonsbooking_sanitizeHTML' ) { if ( isset( $match[0] ) ) { $match = $match[0]; // extract the html before part, looking for {{[*] pattern if ( preg_match( '/\{\{\[([^\]]*)\]/m', $match, $html_before ) === 1 ) { - $html_before = commonsbooking_sanitizeHTML( preg_replace( '/(\{\{)|(\}\})|(\[)|(\])/m', '', $html_before[1] ) ); + $html_before = preg_replace( '/(\{\{)|(\}\})|(\[)|(\])/m', '', $html_before[1] ); + $html_before = call_user_func( $sanitizeFunction, $html_before ); } else { $html_before = ''; } // extract the html after part looking for [*]}} pattern if ( preg_match( '/\[([^\]]*)\]\}\}/m', $match, $html_after ) === 1 ) { - $html_after = commonsbooking_sanitizeHTML( preg_replace( '/(\{\{)|(\}\})|(\[)|(\])/m', '', $html_after[1] ) ); + $html_after = preg_replace( '/(\{\{)|(\}\})|(\[)|(\])/m', '', $html_after[1] ); + $html_after = call_user_func( $sanitizeFunction, $html_after ); } else { $html_after = ''; } @@ -81,7 +85,7 @@ function commonsbooking_parse_template_callback( $match, array $objects = [] ) { $post = $objects[ $path[0] ]; } - $rendered_template_tag = CB::get( commonsbooking_getCBType( $path[0] ), $path[1], $post ); + $rendered_template_tag = CB::get( commonsbooking_getCBType( $path[0] ), $path[1], $post, null, $sanitizeFunction ); if ( $rendered_template_tag !== null && strlen( $rendered_template_tag ) > 0 ) { return $html_before . $rendered_template_tag . $html_after; } else { diff --git a/src/CB/CB.php b/src/CB/CB.php index 8e8044488..20fd4ff40 100644 --- a/src/CB/CB.php +++ b/src/CB/CB.php @@ -25,11 +25,12 @@ public static function getInternalDateFormat(): string { * @param mixed $property * @param \WP_Post|WP_User $wpObject * @param mixed $args + * @param callable $sanitizeFunction The callable used to remove unwanted tags/characters (use default 'commonsbooking_sanitizeHTML' or 'sanitize_text_field') * - * @return mixed + * @return Property of (custom) post (sanitized) or null if not found * @throws Exception */ - public static function get( $key, $property, $wpObject = null, $args = null ) { + public static function get( $key, $property, $wpObject = null, $args = null, $sanitizeFunction = "commonsbooking_sanitizeHTML" ) { // Only CustomPost, WP_User or WP_Post ist allowed. if ( $wpObject && ! ( @@ -49,7 +50,7 @@ public static function get( $key, $property, $wpObject = null, $args = null ) { // If possible cast to CB Custom Post Type Model to get additional functions $wpObject = Helper::castToCBCustomType($wpObject, $key); - $result = self::lookUp( $key, $property, $wpObject, $args ); // Find matching methods, properties or metadata + $result = self::lookUp( $key, $property, $wpObject, $args, $sanitizeFunction ); // Find matching methods, properties or metadata $filterName = sprintf( 'commonsbooking_tag_%s_%s', $key, $property ); return apply_filters( $filterName, $result ); @@ -102,11 +103,12 @@ private static function getPostId( string $key ): ?int { * @param string $property * @param $post * @param $args + * @param callable $sanitizeFunction The callable used to remove unwanted tags/characters * * @return string|null * @throws Exception */ - public static function lookUp( string $key, string $property, $post, $args ): ?string { + public static function lookUp( string $key, string $property, $post, $args, $sanitizeFunction ): ?string { // in any case we need the post object, otherwise we cannot return anything if ( ! $post ) { return null; @@ -120,7 +122,7 @@ public static function lookUp( string $key, string $property, $post, $args ): ?s if ( $result ) { // sanitize output - return commonsbooking_sanitizeHTML( $result ); + return call_user_func( $sanitizeFunction, $result ); } return $result; diff --git a/src/Messages/Message.php b/src/Messages/Message.php index 2ff2287d4..deaa0c541 100644 --- a/src/Messages/Message.php +++ b/src/Messages/Message.php @@ -90,7 +90,7 @@ public function getHeaders() { } public function getSubject() { - return apply_filters( 'commonsbooking_mail_subject', $this->subject, $this->getAction(), 'sanitize_text_field' ); + return apply_filters( 'commonsbooking_mail_subject', $this->subject, $this->getAction() ); } public function getBody() { @@ -132,8 +132,10 @@ protected function prepareMail( } // parse templates & replaces template tags (e.g. {{item:name}}) + // 'body' is HTML. 'subject' is not HTML needs alternative sanitation such that characters like & + // do not get converted to HTML-entities like & $this->body = commonsbooking_sanitizeHTML( commonsbooking_parse_template( $template_body, $objects ) ); - $this->subject = sanitize_text_field( commonsbooking_parse_template( $template_subject, $objects ) ); + $this->subject = sanitize_text_field( commonsbooking_parse_template( $template_subject, $objects, "sanitize_text_field" ) ); // Setup mime type $this->headers[] = "MIME-Version: 1.0"; diff --git a/tests/php/CB/CBTest.php b/tests/php/CB/CBTest.php index ace3f92cb..f25686be6 100644 --- a/tests/php/CB/CBTest.php +++ b/tests/php/CB/CBTest.php @@ -37,19 +37,19 @@ class CBTest extends CustomPostTypeTest { public function testLookUp() { // Test if user meta value is found when handing over WP_Post object $post = get_post( $this->postInstanceId ); - $this->assertEquals( CB::lookUp( 'user', $this->userMetaKey, $post, [] ), $this->userMetaValue ); + $this->assertEquals( CB::lookUp( 'user', $this->userMetaKey, $post, [], 'commonsbooking_sanitizeHTML' ), $this->userMetaValue ); // Test if post title is returned when handing over post key and post object - $this->assertEquals( CB::lookUp( 'post', 'post_title', $post, [] ), $this->postTitle ); + $this->assertEquals( CB::lookUp( 'post', 'post_title', $post, [], 'commonsbooking_sanitizeHTML' ), $this->postTitle ); // Test if null is returned when trying to get not existing property of post - $this->assertEquals( null, CB::lookUp( 'user', 'post_title', $post, [] ) ); + $this->assertEquals( null, CB::lookUp( 'user', 'post_title', $post, [], 'commonsbooking_sanitizeHTML' ) ); // Trying to get property without post object - $this->assertEquals( CB::lookUp( 'user', 'test', null, [] ), null ); - $this->assertEquals( CB::lookUp( 'booking', 'test', null, [] ), null ); - $this->assertEquals( CB::lookUp( 'item', 'test', null, [] ), null ); - $this->assertEquals( CB::lookUp( 'location', 'test', null, [] ), null ); + $this->assertEquals( CB::lookUp( 'user', 'test', null, [], 'commonsbooking_sanitizeHTML' ), null ); + $this->assertEquals( CB::lookUp( 'booking', 'test', null, [], 'commonsbooking_sanitizeHTML' ), null ); + $this->assertEquals( CB::lookUp( 'item', 'test', null, [], 'commonsbooking_sanitizeHTML' ), null ); + $this->assertEquals( CB::lookUp( 'location', 'test', null, [], 'commonsbooking_sanitizeHTML' ), null ); } public function testGet() {