diff --git a/cyr-to-lat.php b/cyr-to-lat.php index 359fe6f..e331bee 100644 --- a/cyr-to-lat.php +++ b/cyr-to-lat.php @@ -10,7 +10,7 @@ * Plugin Name: Cyr-To-Lat * Plugin URI: https://wordpress.org/plugins/cyr2lat/ * Description: Convert Non-Latin characters in post and term slugs to Latin characters. Useful for creating human-readable URLs. Based on the original plugin by Anton Skorobogatov. - * Version: 5.2.2 + * Version: 5.2.3 * Requires at least: 5.1 * Requires PHP: 5.6.20 * Author: Sergey Biryukov, Mikhail Kobzarev, Igor Gergel @@ -36,7 +36,7 @@ /** * Plugin version. */ -define( 'CYR_TO_LAT_VERSION', '5.2.2' ); +define( 'CYR_TO_LAT_VERSION', '5.2.3' ); /** * Path to the plugin dir. diff --git a/readme.txt b/readme.txt index 097f916..194fb48 100644 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,7 @@ Contributors: SergeyBiryukov, mihdan, karevn, webvitaly, kaggdesign Tags: cyrillic, belorussian, ukrainian, bulgarian, macedonian, georgian, kazakh, latin, l10n, russian, cyr-to-lat, cyr2lat, rustolat, slugs, translations, transliteration Requires at least: 5.1 Tested up to: 5.8 -Stable tag: 5.2.2 +Stable tag: 5.2.3 Requires PHP: 5.6.20 Convert Non-Latin characters in post, page and term slugs to Latin characters. @@ -188,6 +188,10 @@ Yes you can! == Changelog == += 5.2.3 (07.09.2021) = +* Fix issue with WP Foro plugin - transliterate topic slug when created on frontend. +* Fix bug with Polylang on REST request. + = 5.2.2 (06.09.2021) = * Fix issue caused by the bug in Jetpack sync. * Optimize code related to WPML locale filtering. diff --git a/src/php/class-main.php b/src/php/class-main.php index 00820e5..db3a499 100644 --- a/src/php/class-main.php +++ b/src/php/class-main.php @@ -114,7 +114,7 @@ class Main { public function __construct() { $this->request = new Request(); - if ( $this->request->is_frontend() ) { + if ( ! $this->request->is_allowed() ) { return; } @@ -149,7 +149,7 @@ public function __construct() { * @noinspection PhpUndefinedClassInspection */ public function init() { - if ( $this->request->is_frontend() ) { + if ( ! $this->request->is_allowed() ) { return; } @@ -533,7 +533,7 @@ public function pll_locale_filter( $locale ) { * @return false|null|string */ private function pll_locale_filter_with_rest() { - if ( ! $this->request->is_rest() ) { + if ( ! defined( 'REST_REQUEST' ) || ! constant( 'REST_REQUEST' ) ) { return null; } diff --git a/src/php/class-request.php b/src/php/class-request.php index af47113..4f83ff8 100644 --- a/src/php/class-request.php +++ b/src/php/class-request.php @@ -14,6 +14,15 @@ */ class Request { + /** + * Is allowed request for plugin to work. + * + * @return bool + */ + public function is_allowed() { + return ! $this->is_frontend() || ( $this->is_frontend() && $this->is_post() ); + } + /** * Is frontend. * @@ -88,4 +97,18 @@ protected function get_rest_route() { return $is_rest ? substr( $current_path, strlen( $rest_path ) ) : ''; } + + /** + * If current request is POST. + * + * @return bool + */ + public function is_post() { + $request_method = filter_var( + isset( $_SERVER['REQUEST_METHOD'] ) ? wp_unslash( $_SERVER['REQUEST_METHOD'] ) : '', + FILTER_SANITIZE_STRING + ); + + return 'POST' === $request_method; + } } diff --git a/tests/phpunit/bootstrap.php b/tests/phpunit/bootstrap.php index f1fe68d..08d8f45 100644 --- a/tests/phpunit/bootstrap.php +++ b/tests/phpunit/bootstrap.php @@ -42,7 +42,7 @@ /** * Plugin version. */ -define( 'CYR_TO_LAT_TEST_VERSION', '5.2.2' ); +const CYR_TO_LAT_TEST_VERSION = '5.2.3'; /** * Path to the plugin dir. diff --git a/tests/phpunit/tests/class-test-main.php b/tests/phpunit/tests/class-test-main.php index 54ebf9d..01fb9be 100644 --- a/tests/phpunit/tests/class-test-main.php +++ b/tests/phpunit/tests/class-test-main.php @@ -60,14 +60,14 @@ public function tearDown(): void { public function test_constructor() { $classname = Main::class; - // Test when requirements are met and not frontend. + // Test when requirements are met and allowed request. $requirements_met = true; - $is_frontend = false; + $is_allowed = true; $request = Mockery::mock( 'overload:' . Request::class ); - $request->shouldReceive( 'is_frontend' )->with()->andReturnUsing( - function () use ( &$is_frontend ) { - return $is_frontend; + $request->shouldReceive( 'is_allowed' )->with()->andReturnUsing( + function () use ( &$is_allowed ) { + return $is_allowed; } ); $request->shouldReceive( 'is_cli' )->with()->andReturn( true ); @@ -125,8 +125,8 @@ function () use ( &$requirements_met ) { self::assertNull( $this->get_protected_property( $mock, 'cli' ) ); self::assertNull( $this->get_protected_property( $mock, 'acf' ) ); - // Test on frontend. - $is_frontend = true; + // Test on not allowed request. + $is_allowed = false; // Get mock, without the constructor being called. $mock = $this->getMockBuilder( $classname )->disableOriginalConstructor()->getMock(); @@ -153,7 +153,7 @@ function () use ( &$requirements_met ) { */ public function test_init() { $request = Mockery::mock( Request::class ); - $request->shouldReceive( 'is_frontend' )->andReturn( false ); + $request->shouldReceive( 'is_allowed' )->andReturn( true ); $request->shouldReceive( 'is_cli' )->andReturn( false ); $subject = Mockery::mock( Main::class )->makePartial(); @@ -164,13 +164,13 @@ public function test_init() { } /** - * Test init() on frontend + * Test init() on allowed request. * * @throws ReflectionException ReflectionException. */ - public function test_init_on_frontend() { + public function test_init_on_allowed_request() { $request = Mockery::mock( Request::class ); - $request->shouldReceive( 'is_frontend' )->andReturn( true ); + $request->shouldReceive( 'is_allowed' )->andReturn( false ); $subject = Mockery::mock( Main::class )->makePartial(); $this->set_protected_property( $subject, 'request', $request ); @@ -186,7 +186,7 @@ public function test_init_on_frontend() { */ public function test_init_with_cli_error() { $request = Mockery::mock( Request::class ); - $request->shouldReceive( 'is_frontend' )->andReturn( false ); + $request->shouldReceive( 'is_allowed' )->andReturn( true ); $request->shouldReceive( 'is_cli' )->andReturn( true ); $subject = Mockery::mock( Main::class )->makePartial(); @@ -213,7 +213,7 @@ function () { */ public function test_init_with_cli() { $request = Mockery::mock( Request::class ); - $request->shouldReceive( 'is_frontend' )->andReturn( false ); + $request->shouldReceive( 'is_allowed' )->andReturn( true ); $request->shouldReceive( 'is_cli' )->andReturn( true ); $subject = Mockery::mock( Main::class )->makePartial(); @@ -951,11 +951,21 @@ public function test_pll_locale_filter_with_rest() { $pll_locale = 'ru'; $data = ''; - $request = Mockery::mock( Request::class ); - $request->shouldReceive( 'is_rest' )->andReturn( true ); - $subject = Mockery::mock( Main::class )->makePartial(); - $this->set_protected_property( $subject, 'request', $request ); + + FunctionMocker::replace( + 'defined', + function ( $constant_name ) { + return 'REST_REQUEST' === $constant_name; + } + ); + + FunctionMocker::replace( + 'constant', + function ( $name ) { + return 'REST_REQUEST' === $name; + } + ); $rest_server = new WP_REST_Server(); WP_Mock::userFunction( 'rest_get_server' )->andReturn( $rest_server ); @@ -973,16 +983,16 @@ function () use ( &$data ) { self::assertSame( $pll_locale, $subject->pll_locale_filter( $locale ) ); // Test that result is cached. - $request->shouldReceive( 'is_rest' )->andReturn( false ); + FunctionMocker::replace( 'defined' ); self::assertSame( $pll_locale, $subject->pll_locale_filter( $locale ) ); } /** - * Test pll_locale_filter() on frontend. + * Test pll_locale_filter() on allowed request. * * @throws ReflectionException ReflectionException. */ - public function test_pll_locale_filter_on_frontend() { + public function test_pll_locale_filter_on_allowed_request() { $locale = 'en_US'; $request = Mockery::mock( Request::class ); diff --git a/tests/phpunit/tests/class-test-request.php b/tests/phpunit/tests/class-test-request.php index c22ca56..28a43ff 100644 --- a/tests/phpunit/tests/class-test-request.php +++ b/tests/phpunit/tests/class-test-request.php @@ -28,11 +28,43 @@ class Test_Request extends Cyr_To_Lat_TestCase { */ public function tearDown(): void { // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited - unset( $_SERVER['REQUEST_URI'], $GLOBALS['wp_rewrite'] ); + unset( $_SERVER['REQUEST_URI'], $_SERVER['REQUEST_METHOD'], $GLOBALS['wp_rewrite'] ); parent::tearDown(); } + /** + * Test is_allowed(). + * + * @param bool $frontend Is frontend. + * @param bool $post Is POST. + * @param bool $expected Expected. + * + * @dataProvider dp_test_is_allowed + * @noinspection PhpUndefinedMethodInspection + */ + public function test_is_allowed( $frontend, $post, $expected ) { + $subject = Mockery::mock( Request::class )->makePartial()->shouldAllowMockingProtectedMethods(); + $subject->shouldReceive( 'is_frontend' )->with()->andReturn( $frontend ); + $subject->shouldReceive( 'is_post' )->with()->andReturn( $post ); + + self::assertSame( $expected, $subject->is_allowed() ); + } + + /** + * Data provider for test_is_allowed(). + * + * @return array + */ + public function dp_test_is_allowed() { + return [ + [ false, false, true ], + [ false, true, true ], + [ true, false, false ], + [ true, true, true ], + ]; + } + /** * Test is_frontend(). * @@ -239,4 +271,20 @@ public function dp_test_get_rest_route() { 'some request' => [ '/some-request', '' ], ]; } + + /** + * Test is_post(). + */ + public function test_is_post() { + WP_Mock::passthruFunction( 'wp_unslash' ); + + $subject = new Request(); + self::assertFalse( $subject->is_post() ); + + $_SERVER['REQUEST_METHOD'] = 'some'; + self::assertFalse( $subject->is_post() ); + + $_SERVER['REQUEST_METHOD'] = 'POST'; + self::assertTrue( $subject->is_post() ); + } }