From a8afd34ce0dbee95c49c14f62ff816a8e9b3982f Mon Sep 17 00:00:00 2001 From: kagg-design Date: Sat, 10 Feb 2024 20:54:43 +0300 Subject: [PATCH] Add description of post types and post statuses on the Converter page. --- readme.txt | 1 + src/php/Main.php | 16 +++ src/php/Requirements.php | 3 - src/php/Settings/Converter.php | 97 +++++++------ tests/unit/MainTest.php | 43 ++++++ tests/unit/Settings/ConverterTest.php | 134 +++++++++++------- .../unit/Settings/PluginSettingsBaseTest.php | 2 +- 7 files changed, 191 insertions(+), 105 deletions(-) diff --git a/readme.txt b/readme.txt index fc19e0e..a5e634e 100644 --- a/readme.txt +++ b/readme.txt @@ -224,6 +224,7 @@ Yes, you can! = 6.0.7 (10.02.2024) = * Tested with WooCommerce 8.5. * Added redirect from the cyrillic post title when creating a new post. +* Added description of post types and post statuses on the Converter page. * Fixed displaying all file descriptions in the Theme Editor in the current locale. = 6.0.6 (14.01.2024) = diff --git a/src/php/Main.php b/src/php/Main.php index 2185a51..4c09bde 100644 --- a/src/php/Main.php +++ b/src/php/Main.php @@ -146,12 +146,28 @@ public function init() { * @return void */ public function init_all() { + $this->load_textdomain(); + $this->init_multilingual(); $this->init_classes(); $this->init_cli(); $this->init_hooks(); } + /** + * Load plugin text domain. + * + * @return void + */ + public function load_textdomain() { + load_default_textdomain(); + load_plugin_textdomain( + 'cyr2lat', + false, + dirname( plugin_basename( constant( 'CYR_TO_LAT_FILE' ) ) ) . '/languages/' + ); + } + /** * Init multilingual features. * It must be first in the init sequence, as we use defined filters internally in our classes. diff --git a/src/php/Requirements.php b/src/php/Requirements.php index e4130b6..362a08b 100644 --- a/src/php/Requirements.php +++ b/src/php/Requirements.php @@ -70,9 +70,6 @@ public function __construct( Settings $settings, AdminNotices $admin_notices, WP */ global $wp_file_descriptions; - $admin_locale = get_locale(); - load_default_textdomain( $admin_locale ); - require_once ABSPATH . 'wp-admin/includes/file.php'; } // @codeCoverageIgnoreEnd diff --git a/src/php/Settings/Converter.php b/src/php/Settings/Converter.php index a2f265f..382284c 100644 --- a/src/php/Settings/Converter.php +++ b/src/php/Settings/Converter.php @@ -65,34 +65,6 @@ protected function init_hooks() { public function init_form_fields() { $this->form_fields = []; - $default_post_types = [ 'post', 'page', 'nav_menu_item' ]; - - $post_types = $default_post_types; - - $filtered_post_types = array_filter( (array) apply_filters( 'ctl_post_types', $post_types ) ); - - $this->form_fields['background_post_types'] = [ - 'label' => __( 'Post Types', 'cyr2lat' ), - 'section' => self::SECTION_TYPES_STATUSES, - 'type' => 'checkbox', - 'placeholder' => '', - 'helper' => __( 'Post types included in the conversion.', 'cyr2lat' ), - 'supplemental' => '', - 'options' => [], - ]; - - foreach ( $post_types as $post_type ) { - $label = $post_type; - - $this->form_fields['background_post_types']['options'][ $post_type ] = $label; - } - - $this->form_fields['background_post_types']['default'] = $default_post_types; - $this->form_fields['background_post_types']['disabled'] = array_diff( $default_post_types, $filtered_post_types ); - - $default_post_statuses = [ 'publish', 'future', 'private' ]; - $post_statuses = [ 'publish', 'future', 'private', 'draft', 'pending' ]; - $this->form_fields['background_post_statuses'] = [ 'label' => __( 'Post Statuses', 'cyr2lat' ), 'section' => self::SECTION_TYPES_STATUSES, @@ -103,46 +75,79 @@ public function init_form_fields() { 'options' => [], ]; - foreach ( $post_statuses as $post_status ) { - $label = $post_status; - - $this->form_fields['background_post_statuses']['options'][ $post_status ] = $label; + $post_status_objects = get_post_stati( [ 'internal' => false ], 'objects' ); + $post_stati = array_keys( $post_status_objects ); + $default_post_statuses = array_intersect( $post_stati, [ 'publish', 'future', 'private' ] ); + $array_flip = array_flip( $default_post_statuses ); + $post_status_objects = + array_intersect_key( $post_status_objects, $array_flip ) + + array_diff_key( $post_status_objects, $array_flip ); + + foreach ( $post_status_objects as $post_status => $post_status_object ) { + $this->form_fields['background_post_statuses']['options'][ $post_status ] = + // phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText + __( $post_status_object->label ) . ' (' . $post_status . ')'; } $this->form_fields['background_post_statuses']['default'] = $default_post_statuses; } + /** + * Get convertible post type objects. + * + * @return array + */ + private static function get_get_convertible_post_type_objects(): array { + return get_post_types( + [ + 'public' => true, + 'name' => 'nav_menu_item', + ], + 'objects', + 'or' + ); + } + /** * Get convertible post types. * * @return array */ public static function get_convertible_post_types(): array { - $post_types = get_post_types( [ 'public' => true ] ); - - return array_merge( $post_types, [ 'nav_menu_item' => 'nav_menu_item' ] ); + return array_keys( self::get_get_convertible_post_type_objects() ); } /** * Init form fields. */ public function delayed_init_form_fields() { - $post_types = self::get_convertible_post_types(); + $this->form_fields['background_post_types'] = [ + 'label' => __( 'Post Types', 'cyr2lat' ), + 'section' => self::SECTION_TYPES_STATUSES, + 'type' => 'checkbox', + 'placeholder' => '', + 'helper' => __( 'Post types included in the conversion.', 'cyr2lat' ), + 'supplemental' => '', + 'options' => [], + ]; + $post_type_objects = self::get_get_convertible_post_type_objects(); + $post_types = array_keys( $post_type_objects ); + $default_post_types = array_intersect( $post_types, [ 'post', 'page', 'nav_menu_item' ] ); + $array_flip = array_flip( $default_post_types ); + $post_type_objects = + array_intersect_key( $post_type_objects, $array_flip ) + + array_diff_key( $post_type_objects, $array_flip ); $filtered_post_types = array_filter( (array) apply_filters( 'ctl_post_types', $post_types ) ); - $this->form_fields['background_post_types']['options'] = []; - - foreach ( $post_types as $post_type ) { - $label = $post_type; - - $this->form_fields['background_post_types']['options'][ $post_type ] = $label; + foreach ( $post_type_objects as $post_type => $post_type_object ) { + $this->form_fields['background_post_types']['options'][ $post_type ] = + // phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText + __( $post_type_object->label ) . ' (' . $post_type . ')'; } - $this->form_fields['background_post_types']['disabled'] = array_diff( - $this->form_fields['background_post_types']['default'], - $filtered_post_types - ); + $this->form_fields['background_post_types']['default'] = $default_post_types; + $this->form_fields['background_post_types']['disabled'] = array_diff( $default_post_types, $filtered_post_types ); } /** diff --git a/tests/unit/MainTest.php b/tests/unit/MainTest.php index 69bce95..4890833 100644 --- a/tests/unit/MainTest.php +++ b/tests/unit/MainTest.php @@ -77,6 +77,7 @@ public function test_init() { * @return void */ public function test_init_all() { + $load_textdomain = 'load_textdomain'; $init_multilingual = 'init_multilingual'; $init_classes = 'init_classes'; $init_cli = 'init_cli'; @@ -85,6 +86,7 @@ public function test_init_all() { $subject = Mockery::mock( Main::class )->makePartial(); $subject->shouldAllowMockingProtectedMethods(); + $subject->shouldReceive( $load_textdomain )->once(); $subject->shouldReceive( $init_multilingual )->once(); $subject->shouldReceive( $init_classes )->once(); $subject->shouldReceive( $init_cli )->once(); @@ -93,6 +95,32 @@ public function test_init_all() { $subject->init_all(); } + /** + * Test load_textdomain(). + * + * @return void + */ + public function test_load_textdomain() { + $plugin_file = '/var/www/wp-content/plugins/cyr2lat/cyr-to-lat.php'; + $plugin_base_name = 'cyr2lat/cyr-to-lat.php'; + + FunctionMocker::replace( + 'constant', + static function ( $name ) use ( $plugin_file ) { + return $name === 'CYR_TO_LAT_FILE' ? $plugin_file : ''; + } + ); + + WP_Mock::userFunction( 'plugin_basename' )->with( $plugin_file )->once()->andReturn( $plugin_base_name ); + WP_Mock::userFunction( 'load_default_textdomain' )->with()->once(); + WP_Mock::userFunction( 'load_plugin_textdomain' ) + ->with( 'cyr2lat', false, 'cyr2lat/languages/' ) + ->once(); + $subject = Mockery::mock( Main::class )->makePartial(); + + $subject->load_textdomain(); + } + /** * Test init_multilingual. * @@ -416,6 +444,21 @@ public function test_init_hooks_when_not_allowed() { $subject->$method(); } + /** + * Test settings(). + * + * @throws ReflectionException ReflectionException. + */ + public function test_settings() { + $settings = Mockery::mock( Settings::class ); + + $subject = Mockery::mock( Main::class )->makePartial(); + + $this->set_protected_property( $subject, 'settings', $settings ); + + self::assertSame( $settings, $subject->settings() ); + } + /** * Test that sanitize_title() does nothing when title is empty. */ diff --git a/tests/unit/Settings/ConverterTest.php b/tests/unit/Settings/ConverterTest.php index b6722c3..58fc93c 100644 --- a/tests/unit/Settings/ConverterTest.php +++ b/tests/unit/Settings/ConverterTest.php @@ -163,24 +163,6 @@ public function test_init_hooks() { */ public function test_init_form_fields() { $expected = [ - 'background_post_types' => - [ - 'label' => 'Post Types', - 'section' => 'types-statuses', - 'type' => 'checkbox', - 'placeholder' => '', - 'helper' => 'Post types included in the conversion.', - 'supplemental' => '', - 'options' => - [ - 'post' => 'post', - 'page' => 'page', - 'nav_menu_item' => 'nav_menu_item', - ], - 'default' => - [ 'post', 'page', 'nav_menu_item' ], - 'disabled' => [], - ], 'background_post_statuses' => [ 'label' => 'Post Statuses', @@ -191,17 +173,26 @@ public function test_init_form_fields() { 'supplemental' => '', 'options' => [ - 'publish' => 'publish', - 'future' => 'future', - 'private' => 'private', - 'draft' => 'draft', - 'pending' => 'pending', + 'publish' => 'Published (publish)', + 'future' => 'Scheduled (future)', + 'private' => 'Private (private)', + 'draft' => 'Draft (draft)', + 'pending' => 'Pending (pending)', ], 'default' => [ 'publish', 'future', 'private' ], ], ]; + $post_status_objects = [ + 'publish' => (object) [ 'label' => 'Published' ], + 'future' => (object) [ 'label' => 'Scheduled' ], + 'private' => (object) [ 'label' => 'Private' ], + 'draft' => (object) [ 'label' => 'Draft' ], + 'pending' => (object) [ 'label' => 'Pending' ], + ]; + + WP_Mock::userFunction( 'get_post_stati' )->with( [ 'internal' => false ], 'objects' )->andReturn( $post_status_objects ); $mock = Mockery::mock( Converter::class )->makePartial()->shouldAllowMockingProtectedMethods(); $mock->init_form_fields(); @@ -214,18 +205,28 @@ public function test_init_form_fields() { */ public function test_get_convertible_post_types() { $post_types = [ - 'post' => 'post', - 'page' => 'page', - 'attachment' => 'attachment', + 'post' => (object) [ 'post' ], + 'page' => (object) [ 'page' ], + 'attachment' => (object) [ 'attachment' ], + 'nav_menu_item' => (object) [ 'nav_menu_item' ], ]; $expected = [ - 'post' => 'post', - 'page' => 'page', - 'attachment' => 'attachment', - 'nav_menu_item' => 'nav_menu_item', + 'post', + 'page', + 'attachment', + 'nav_menu_item', ]; - WP_Mock::userFunction( 'get_post_types' )->with( [ 'public' => true ] )->andReturn( $post_types ); + WP_Mock::userFunction( 'get_post_types' ) + ->with( + [ + 'public' => true, + 'name' => 'nav_menu_item', + ], + 'objects', + 'or' + ) + ->andReturn( $post_types ); $subject = Mockery::mock( Converter::class )->makePartial()->shouldAllowMockingProtectedMethods(); @@ -238,53 +239,76 @@ public function test_get_convertible_post_types() { * @throws ReflectionException ReflectionException. */ public function test_delayed_init_form_fields() { - $post_types = [ - 'post' => 'post', - 'page' => 'page', - 'attachment' => 'attachment', + $post_types = [ + 'post' => (object) [ 'label' => 'Posts' ], + 'page' => (object) [ 'label' => 'Pages' ], + 'attachment' => (object) [ 'label' => 'Media' ], + 'nav_menu_item' => (object) [ 'label' => 'Navigation Menu Items' ], ]; - $expected = [ - 'background_post_types' => + $post_status_objects = [ + 'publish' => (object) [ 'label' => 'Published' ], + 'future' => (object) [ 'label' => 'Scheduled' ], + 'private' => (object) [ 'label' => 'Private' ], + 'draft' => (object) [ 'label' => 'Draft' ], + 'pending' => (object) [ 'label' => 'Pending' ], + ]; + $expected = [ + 'background_post_statuses' => [ - 'label' => 'Post Types', + 'label' => 'Post Statuses', 'section' => 'types-statuses', 'type' => 'checkbox', 'placeholder' => '', - 'helper' => 'Post types included in the conversion.', + 'helper' => 'Post statuses included in the conversion.', 'supplemental' => '', 'options' => [ - 'post' => 'post', - 'page' => 'page', - 'attachment' => 'attachment', - 'nav_menu_item' => 'nav_menu_item', + 'publish' => 'Published (publish)', + 'future' => 'Scheduled (future)', + 'private' => 'Private (private)', + 'draft' => 'Draft (draft)', + 'pending' => 'Pending (pending)', ], 'default' => - [ 'post', 'page', 'nav_menu_item' ], - 'disabled' => [], + [ 'publish', 'future', 'private' ], ], - 'background_post_statuses' => + 'background_post_types' => [ - 'label' => 'Post Statuses', + 'label' => 'Post Types', 'section' => 'types-statuses', 'type' => 'checkbox', 'placeholder' => '', - 'helper' => 'Post statuses included in the conversion.', + 'helper' => 'Post types included in the conversion.', 'supplemental' => '', 'options' => [ - 'publish' => 'publish', - 'future' => 'future', - 'private' => 'private', - 'draft' => 'draft', - 'pending' => 'pending', + 'post' => 'Posts (post)', + 'page' => 'Pages (page)', + 'nav_menu_item' => 'Navigation Menu Items (nav_menu_item)', + 'attachment' => 'Media (attachment)', ], 'default' => - [ 'publish', 'future', 'private' ], + [ + 0 => 'post', + 1 => 'page', + 3 => 'nav_menu_item', + ], + 'disabled' => [], ], ]; - WP_Mock::userFunction( 'get_post_types' )->with( [ 'public' => true ] )->andReturn( $post_types ); + WP_Mock::userFunction( 'get_post_stati' )->with( [ 'internal' => false ], 'objects' ) + ->andReturn( $post_status_objects ); + WP_Mock::userFunction( 'get_post_types' ) + ->with( + [ + 'public' => true, + 'name' => 'nav_menu_item', + ], + 'objects', + 'or' + ) + ->andReturn( $post_types ); $subject = Mockery::mock( Converter::class )->makePartial()->shouldAllowMockingProtectedMethods(); diff --git a/tests/unit/Settings/PluginSettingsBaseTest.php b/tests/unit/Settings/PluginSettingsBaseTest.php index 1eb1273..ebef87a 100644 --- a/tests/unit/Settings/PluginSettingsBaseTest.php +++ b/tests/unit/Settings/PluginSettingsBaseTest.php @@ -34,7 +34,7 @@ class PluginSettingsBaseTest extends CyrToLatTestCase { */ public function test_plugin_basename() { $plugin_file = '/var/www/wp-content/plugins/cyr2lat/cyr-to-lat.php'; - $plugin_base_name = 'cyr2lat/cur-to-lat.php'; + $plugin_base_name = 'cyr2lat/cyr-to-lat.php'; $subject = Mockery::mock( PluginSettingsBase::class )->makePartial()->shouldAllowMockingProtectedMethods(); $method = 'plugin_basename';