diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c55d0d5..d5ea3a7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,6 +26,7 @@ jobs: - name: Install dependencies and update PHPUnit run: | + composer config github-oauth.github.com ${{ secrets.GITHUB_TOKEN }} composer install make update-phpunit env: diff --git a/cyr-to-lat.php b/cyr-to-lat.php index a19359a..0494630 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.0 + * Version: 5.2.1 * 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.0' ); +define( 'CYR_TO_LAT_VERSION', '5.2.1' ); /** * Path to the plugin dir. diff --git a/readme.txt b/readme.txt index 1191056..02f522d 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.0 +Stable tag: 5.2.1 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.1 (29.07.2021) = +* Determine WPML language only once to improve performance. +* Avoid notice on bad SQL request when taxonomies are empty. + = 5.2.0 (27.07.2021) = * Add support for categories and tags in other languages with wpml. diff --git a/src/php/class-main.php b/src/php/class-main.php index 83d90ff..a36d789 100644 --- a/src/php/class-main.php +++ b/src/php/class-main.php @@ -92,7 +92,14 @@ class Main { * * @var string */ - private $pll_locale = false; + private $pll_locale; + + /** + * WPML locale. + * + * @var string + */ + private $wpml_locale; /** * Main constructor. @@ -197,13 +204,16 @@ public function sanitize_title( $title, $raw_title = '', $context = '' ) { $term = ''; if ( $this->is_term ) { - $sql = $wpdb->prepare( + $sql = $wpdb->prepare( "SELECT slug FROM $wpdb->terms t LEFT JOIN $wpdb->term_taxonomy tt ON t.term_id = tt.term_id WHERE t.name = %s", $title ); - $sql .= ' AND tt.taxonomy IN (' . $this->prepare_in( $this->taxonomies ) . ')'; + + if ( $this->taxonomies ) { + $sql .= ' AND tt.taxonomy IN (' . $this->prepare_in( $this->taxonomies ) . ')'; + } // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared // phpcs:ignore WordPress.DB.DirectDatabaseQuery @@ -434,8 +444,8 @@ public function sanitize_post_name( $data, $postarr = [] ) { public function pre_insert_term_filter( $term, $taxonomy ) { if ( 0 === $term || - '' === trim( $term ) || - is_wp_error( $term ) + is_wp_error( $term ) || + '' === trim( $term ) ) { return $term; } @@ -595,11 +605,17 @@ private function pll_locale_filter_with_term() { * @return string */ public function wpml_locale_filter( $locale ) { + if ( $this->wpml_locale ) { + return $this->wpml_locale; + } + $language_code = wpml_get_current_language(); $languages = apply_filters( 'wpml_active_languages', null ); if ( isset( $languages[ $language_code ] ) ) { - return $languages[ $language_code ]['default_locale']; + $this->wpml_locale = $languages[ $language_code ]['default_locale']; + + return $this->wpml_locale; } return $locale; diff --git a/tests/phpunit/bootstrap.php b/tests/phpunit/bootstrap.php index 4418110..4eb0f2b 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.0' ); +define( 'CYR_TO_LAT_TEST_VERSION', '5.2.1' ); /** * Path to the plugin dir. diff --git a/tests/phpunit/tests/class-test-main.php b/tests/phpunit/tests/class-test-main.php index 34ff7f8..bc05fcb 100644 --- a/tests/phpunit/tests/class-test-main.php +++ b/tests/phpunit/tests/class-test-main.php @@ -372,27 +372,33 @@ public function dp_test_sanitize_title() { } /** - * Test sanitize_title() for terms + * Test sanitize_title() for insert_term * - * @param string $title Title to sanitize. - * @param string $term Term to sanitize. - * @param string $expected Expected result. + * @param string $title Title to sanitize. + * @param string|int|object $term Term to use. + * @param string $expected Expected result. * - * @dataProvider dp_test_sanitize_title_for_insert_term_and_get_terms + * @dataProvider dp_test_sanitize_title_for_insert_term * @throws ReflectionException ReflectionException. */ - public function test_sanitize_title_for_insert_term_and_get_terms( $title, $term, $expected ) { + public function test_sanitize_title_for_insert_term( $title, $term, $expected ) { global $wpdb; - $taxonomy = 'category'; - $prepared_tax = '\'category\''; + $taxonomy = 'taxonomy'; + $prepared_tax = '\'' . $taxonomy . '\''; $subject = $this->get_subject(); - WP_Mock::userFunction( 'is_wp_error' )->with( $term )->andReturn( false ); - WP_Mock::onFilter( 'ctl_pre_sanitize_title' )->with( false, urldecode( $title ) )->reply( false ); + $times = $term ? 1 : 0; - $times = $term ? 2 : 1; + if ( is_object( $term ) ) { + WP_Mock::userFunction( 'is_wp_error' )->with( $term )->andReturn( true ); + $times = 0; + } else { + WP_Mock::userFunction( 'is_wp_error' )->with( $term )->andReturn( false ); + } + + WP_Mock::onFilter( 'ctl_pre_sanitize_title' )->with( false, urldecode( $title ) )->reply( false ); $subject->shouldReceive( 'prepare_in' )->times( $times )->with( [ $taxonomy ] )->andReturn( $prepared_tax ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited @@ -418,20 +424,81 @@ public function test_sanitize_title_for_insert_term_and_get_terms( $title, $term self::assertSame( $expected, $subject->sanitize_title( $title ) ); // Make sure we search in the db only once being called from wp_insert_term(). self::assertSame( $title, $subject->sanitize_title( $title ) ); + } + + /** + * Data provider for test_sanitize_title_for_insert_term() + */ + public function dp_test_sanitize_title_for_insert_term() { + return [ + [ 'title', 'term', 'term' ], + [ 'title', '', 'title' ], + [ 'title', 0, 'title' ], + [ 'title', (object) [], 'title' ], + ]; + } + + /** + * Test sanitize_title() for get_terms + * + * @param string $title Title to sanitize. + * @param string $term Term to us. + * @param array $taxonomies Taxonomies to use. + * @param string $prepared_taxonomies Prepared taxonomies to use. + * @param string $expected Expected result. + * + * @dataProvider dp_test_sanitize_title_for_get_terms + * @throws ReflectionException ReflectionException. + */ + public function test_sanitize_title_for_get_terms( $title, $term, $taxonomies, $prepared_taxonomies, $expected ) { + global $wpdb; + + $subject = $this->get_subject(); + + $times = $taxonomies ? 1 : 0; - $subject->get_terms_args_filter( [ 'some args' ], [ $taxonomy ] ); + WP_Mock::onFilter( 'ctl_pre_sanitize_title' )->with( false, urldecode( $title ) )->reply( false ); + + $subject->shouldReceive( 'prepare_in' )->times( $times )->with( $taxonomies ) + ->andReturn( $prepared_taxonomies ); + // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited + $wpdb = Mockery::mock( wpdb::class ); + $wpdb->terms = 'wp_terms'; + $wpdb->term_taxonomy = 'wp_term_taxonomy'; + + $request = "SELECT slug FROM {$wpdb->terms} t LEFT JOIN {$wpdb->term_taxonomy} tt + ON t.term_id = tt.term_id + WHERE t.name = %s"; + $prepared_request = 'SELECT slug FROM ' . $wpdb->terms . " t LEFT JOIN {$wpdb->term_taxonomy} tt + ON t.term_id = tt.term_id + WHERE t.name = " . $title; + + $sql = $prepared_request; + + if ( $taxonomies ) { + $sql .= ' AND tt.taxonomy IN (' . $prepared_taxonomies . ')'; + } + + $wpdb->shouldReceive( 'prepare' )->once()->with( + $request, + $title + )->andReturn( $prepared_request ); + $wpdb->shouldReceive( 'get_var' )->once()->with( $sql )->andReturn( $term ); + + $subject->get_terms_args_filter( [ 'some args' ], $taxonomies ); self::assertSame( $expected, $subject->sanitize_title( $title ) ); // Make sure we search in the db only once being called from wp_insert_term(). self::assertSame( $title, $subject->sanitize_title( $title ) ); } /** - * Data provider for wp_insert_term() + * Data provider for test_sanitize_title_for_get_terms() */ - public function dp_test_sanitize_title_for_insert_term_and_get_terms() { + public function dp_test_sanitize_title_for_get_terms() { return [ - [ 'title', 'term', 'term' ], - [ 'title', '', 'title' ], + [ 'title', 'term', [ 'taxonomy' ], "'taxonomy'", 'term' ], + [ 'title', 'term', [ 'taxonomy1', 'taxonomy2' ], "'taxonomy1', 'taxonomy2'", 'term' ], + [ 'title', 'term', [], '', 'term' ], ]; } @@ -1148,12 +1215,17 @@ public function test_wpml_locale_filter( $locale, $language_code, $expected ) { ], ]; - WP_Mock::userFunction( 'wpml_get_current_language' )->with()->andReturn( $language_code ); + $times = array_key_exists( $language_code, $languages ) ? 1 : 2; + + WP_Mock::userFunction( 'wpml_get_current_language' )->times( $times )->with()->andReturn( $language_code ); WP_Mock::onFilter( 'wpml_active_languages' )->with( null )->reply( $languages ); $subject = Mockery::mock( Main::class )->makePartial(); self::assertSame( $expected, $subject->wpml_locale_filter( $locale ) ); + + // Make sure that we do call wpml_get_current_language() anymore if language code exists. + self::assertSame( $expected, $subject->wpml_locale_filter( $locale ) ); } /**