From 682d32bfe29b8c8234d81128a6abc7945e020d16 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 19 Aug 2019 15:26:50 -0600 Subject: [PATCH 01/31] dt_distribute_post_status first pass --- .../WordPressExternalConnection.php | 2 ++ .../NetworkSiteConnection.php | 18 ++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/includes/classes/ExternalConnections/WordPressExternalConnection.php b/includes/classes/ExternalConnections/WordPressExternalConnection.php index 357982d68..c6cd5c229 100644 --- a/includes/classes/ExternalConnections/WordPressExternalConnection.php +++ b/includes/classes/ExternalConnections/WordPressExternalConnection.php @@ -546,6 +546,8 @@ public function push( $post_id, $args = array() ) { $signature = \Distributor\Subscriptions\generate_signature(); + $distribute_post_status = apply_filters( 'dt_distribute_post_status', false ); + /** * Now let's push */ diff --git a/includes/classes/InternalConnections/NetworkSiteConnection.php b/includes/classes/InternalConnections/NetworkSiteConnection.php index 6e3b8bc39..a748bbb2f 100644 --- a/includes/classes/InternalConnections/NetworkSiteConnection.php +++ b/includes/classes/InternalConnections/NetworkSiteConnection.php @@ -68,6 +68,14 @@ public function push( $post_id, $args = array() ) { $original_post_url = get_permalink( $post_id ); $using_gutenberg = \Distributor\Utils\is_using_gutenberg( $post ); + /** + * Filter whether Distributor should update post statuses when the origin post status changes. + * + * False by default, return true to have post statuses distributed. + * + */ + $distribute_post_status = apply_filters( 'dt_distribute_post_status', false ); + $new_post_args = array( 'post_title' => get_the_title( $post_id ), 'post_name' => $post->post_name, @@ -75,7 +83,7 @@ public function push( $post_id, $args = array() ) { 'post_excerpt' => $post->post_excerpt, 'post_type' => $post->post_type, 'post_author' => get_current_user_id(), - 'post_status' => 'publish', + 'post_status' => $distribute_post_status ? $post->post_status : 'publish', ); $media = \Distributor\Utils\prepare_media( $post_id ); @@ -100,10 +108,12 @@ public function push( $post_id, $args = array() ) { if ( empty( $args['post_status'] ) ) { if ( isset( $new_post_args['ID'] ) ) { + if ( ! $distribute_post_status ) { // Avoid updating the status of previously distributed posts. - $existing_status = get_post_status( (int) $new_post_args['ID'] ); - if ( $existing_status ) { - $new_post_args['post_status'] = $existing_status; + $existing_status = get_post_status( (int) $new_post_args['ID'] ); + if ( $existing_status ) { + $new_post_args['post_status'] = $existing_status; + } } } } else { From b8c69884536dc92c57df3d46e95408e4afd50b01 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 19 Aug 2019 16:20:07 -0600 Subject: [PATCH 02/31] distribute status to external connections --- .../ExternalConnections/WordPressExternalConnection.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/includes/classes/ExternalConnections/WordPressExternalConnection.php b/includes/classes/ExternalConnections/WordPressExternalConnection.php index c6cd5c229..acd6e9121 100644 --- a/includes/classes/ExternalConnections/WordPressExternalConnection.php +++ b/includes/classes/ExternalConnections/WordPressExternalConnection.php @@ -547,6 +547,7 @@ public function push( $post_id, $args = array() ) { $signature = \Distributor\Subscriptions\generate_signature(); $distribute_post_status = apply_filters( 'dt_distribute_post_status', false ); + $distribute_status = $distribute_post_status ? $post->post_status ? 'publish'; /** * Now let's push @@ -556,7 +557,7 @@ public function push( $post_id, $args = array() ) { 'slug' => $post->post_name, 'content' => Utils\get_processed_content( $post->post_content ), 'type' => $post->post_type, - 'status' => ( ! empty( $args['post_status'] ) ) ? $args['post_status'] : 'publish', + 'status' => ( ! empty( $args['post_status'] ) ) ? $args['post_status'] : $distribute_status, 'excerpt' => $post->post_excerpt, 'distributor_original_source_id' => $this->id, 'distributor_original_site_name' => get_bloginfo( 'name' ), From c0a23bcb7b3aa2ca21bba5033f90fe64a0d73e42 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 20 Aug 2019 14:28:53 -0600 Subject: [PATCH 03/31] fix typo --- .../classes/ExternalConnections/WordPressExternalConnection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/classes/ExternalConnections/WordPressExternalConnection.php b/includes/classes/ExternalConnections/WordPressExternalConnection.php index acd6e9121..2adfb9ceb 100644 --- a/includes/classes/ExternalConnections/WordPressExternalConnection.php +++ b/includes/classes/ExternalConnections/WordPressExternalConnection.php @@ -547,7 +547,7 @@ public function push( $post_id, $args = array() ) { $signature = \Distributor\Subscriptions\generate_signature(); $distribute_post_status = apply_filters( 'dt_distribute_post_status', false ); - $distribute_status = $distribute_post_status ? $post->post_status ? 'publish'; + $distribute_status = $distribute_post_status ? $post->post_status : 'publish'; /** * Now let's push From d59c76590aeb7852a0f55993fb35ffb0cf15817e Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Wed, 21 Aug 2019 11:48:39 -0600 Subject: [PATCH 04/31] changes for phpcs --- .../WordPressDotcomOauth2Authentication.php | 8 ++++---- .../ExternalConnections/WordPressExternalConnection.php | 2 +- .../classes/InternalConnections/NetworkSiteConnection.php | 4 ++-- includes/syndicated-post-ui.php | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/includes/classes/Authentications/WordPressDotcomOauth2Authentication.php b/includes/classes/Authentications/WordPressDotcomOauth2Authentication.php index 97a804b5f..1b24db2d9 100644 --- a/includes/classes/Authentications/WordPressDotcomOauth2Authentication.php +++ b/includes/classes/Authentications/WordPressDotcomOauth2Authentication.php @@ -51,7 +51,7 @@ class WordPressDotcomOauth2Authentication extends Authentication { public static function credentials_form( $args = array() ) { // Check if we need to display the form, or request a token? - $code = isset( $_GET['code'] ) ? sanitize_text_field( wp_unslash( $_GET['code'] ) ) : false; // Input var okay. WPCS: CSRF ok. + $code = isset( $_GET['code'] ) ? sanitize_text_field( wp_unslash( $_GET['code'] ) ) : false; // @codingStandardsIgnoreLine No nonce needed. /** * A code is present as a query parameter in the URL when the user has authorized the connection @@ -81,10 +81,10 @@ public static function credentials_form( $args = array() ) { // Calculate the redirect_uri to use for authorization (the current admin url & query vars). $redirect_uri = esc_url( ( is_ssl() ? 'https://' : 'http://' ) . - sanitize_text_field( isset( $_SERVER['HTTP_HOST'] ) ? $_SERVER['HTTP_HOST'] : '' ) . // Input var okay. WPCS: CSRF ok. - sanitize_text_field( isset( $_SERVER['SCRIPT_NAME'] ) ? $_SERVER['SCRIPT_NAME'] : '' ) . // WPCS: input var ok. + sanitize_text_field( isset( $_SERVER['HTTP_HOST'] ) ? $_SERVER['HTTP_HOST'] : '' ) . + sanitize_text_field( isset( $_SERVER['SCRIPT_NAME'] ) ? $_SERVER['SCRIPT_NAME'] : '' ) . '?' . - sanitize_text_field( isset( $_SERVER['QUERY_STRING'] ) ? $_SERVER['QUERY_STRING'] : '' ) // WPCS: input var ok. + sanitize_text_field( isset( $_SERVER['QUERY_STRING'] ) ? $_SERVER['QUERY_STRING'] : '' ) ); $args[ self::API_REDIRECT_URI ] = $redirect_uri; diff --git a/includes/classes/ExternalConnections/WordPressExternalConnection.php b/includes/classes/ExternalConnections/WordPressExternalConnection.php index 2adfb9ceb..094969a00 100644 --- a/includes/classes/ExternalConnections/WordPressExternalConnection.php +++ b/includes/classes/ExternalConnections/WordPressExternalConnection.php @@ -547,7 +547,7 @@ public function push( $post_id, $args = array() ) { $signature = \Distributor\Subscriptions\generate_signature(); $distribute_post_status = apply_filters( 'dt_distribute_post_status', false ); - $distribute_status = $distribute_post_status ? $post->post_status : 'publish'; + $distribute_status = $distribute_post_status ? $post->post_status : 'publish'; /** * Now let's push diff --git a/includes/classes/InternalConnections/NetworkSiteConnection.php b/includes/classes/InternalConnections/NetworkSiteConnection.php index a748bbb2f..6d135a9be 100644 --- a/includes/classes/InternalConnections/NetworkSiteConnection.php +++ b/includes/classes/InternalConnections/NetworkSiteConnection.php @@ -72,7 +72,6 @@ public function push( $post_id, $args = array() ) { * Filter whether Distributor should update post statuses when the origin post status changes. * * False by default, return true to have post statuses distributed. - * */ $distribute_post_status = apply_filters( 'dt_distribute_post_status', false ); @@ -109,7 +108,8 @@ public function push( $post_id, $args = array() ) { if ( isset( $new_post_args['ID'] ) ) { if ( ! $distribute_post_status ) { - // Avoid updating the status of previously distributed posts. + + // Avoid updating the status of previously distributed posts. $existing_status = get_post_status( (int) $new_post_args['ID'] ); if ( $existing_status ) { $new_post_args['post_status'] = $existing_status; diff --git a/includes/syndicated-post-ui.php b/includes/syndicated-post-ui.php index 0f40a4446..2e62fb47a 100644 --- a/includes/syndicated-post-ui.php +++ b/includes/syndicated-post-ui.php @@ -26,7 +26,7 @@ function() { add_filter( 'admin_body_class', __NAMESPACE__ . '\add_linked_class' ); add_filter( 'post_row_actions', __NAMESPACE__ . '\remove_quick_edit', 10, 2 ); - $post = isset( $_GET['post'] ) ? get_post( (int) $_GET['post'] ) : false; + $post = isset( $_GET['post'] ) ? get_post( (int) $_GET['post'] ) : false; // @codingStandardsIgnoreLine No nonce needed. if ( $post && ! \Distributor\Utils\is_using_gutenberg( $post ) ) { add_action( 'do_meta_boxes', __NAMESPACE__ . '\replace_revisions_meta_box', 10, 3 ); From a92e40d0c9bc33104e33c1f6d70b3def5af57888 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 15 Oct 2019 08:02:14 -0600 Subject: [PATCH 05/31] status distribution tests part 1, add helper plugin --- .../enable-post-status-distribution.php | 17 ++++ tests/wpacceptance/DistributedPostTest.php | 79 ++++++++++++++++++- wpacceptance.json | 6 +- 3 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 tests/test-plugins/enable-post-status-distribution/enable-post-status-distribution.php diff --git a/tests/test-plugins/enable-post-status-distribution/enable-post-status-distribution.php b/tests/test-plugins/enable-post-status-distribution/enable-post-status-distribution.php new file mode 100644 index 000000000..63d3d79d9 --- /dev/null +++ b/tests/test-plugins/enable-post-status-distribution/enable-post-status-distribution.php @@ -0,0 +1,17 @@ +pushPost( $I, 40, 2 ); @@ -102,4 +100,81 @@ public function testDistributedFrom() { $this->assertTrue( ( false !== strpos( $source, 'waitUntilElementVisible( '#message' ); + + // Update the origin post + $I->moveTo( $post_info['original_edit_url'] ); + $I->waitUntilElementVisible( 'body.post-php' ); + + // The origin post should be in a draft state. + $I->seeText( 'Draft', '#post-status-display' ); + + // Change the remote post title and update. + $I->typeInField( '#title', 'New test title' ); + $I->click( '#publish' ); + $I->waitUntilElementVisible( '#wpadminbar' ); + + // The remote post should now in a draft status, the post status is distributed. + $I->moveTo( $post_info['distributed_edit_url'] ); + $I->waitUntilElementVisible( 'body.post-php' ); + $I->seeText( 'Draft', '#post-status-display' ); + + } + } diff --git a/wpacceptance.json b/wpacceptance.json index 6d79be2b6..79f3830ef 100644 --- a/wpacceptance.json +++ b/wpacceptance.json @@ -9,5 +9,9 @@ ], "enforce_clean_db": true, "bootstrap": "tests\/wpacceptance\/bootstrap.php", - "repository": "10up" + "repository": "10up", + "before_scripts" : [ + "mv tests/test-plugins/* ../" + ], + "project_path": "%WP_ROOT%/wp-content" } From 51efd70e3f6b73f959902b2a714b3c7334750799 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 15 Oct 2019 08:36:41 -0600 Subject: [PATCH 06/31] complete testPushStatusDistribution --- tests/wpacceptance/DistributedPostTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/wpacceptance/DistributedPostTest.php b/tests/wpacceptance/DistributedPostTest.php index 3f5d86950..d315ce620 100644 --- a/tests/wpacceptance/DistributedPostTest.php +++ b/tests/wpacceptance/DistributedPostTest.php @@ -104,7 +104,7 @@ public function testDistributedFrom() { /** * Test network push status updates and the `dt_distribute_post_status` filter. */ - public function testPushDistributedStatus() { + public function testPushStatusDistribution() { $I = $this->openBrowserPage(); $I->loginAs( 'wpsnapshots' ); @@ -166,8 +166,8 @@ public function testPushDistributedStatus() { $I->seeText( 'Draft', '#post-status-display' ); // Change the remote post title and update. - $I->typeInField( '#title', 'New test title' ); - $I->click( '#publish' ); + $I->typeInField( '#title', 'Updated test title' ); + $I->click( '#save-post' ); $I->waitUntilElementVisible( '#wpadminbar' ); // The remote post should now in a draft status, the post status is distributed. From 5e7c887937af3aaf523895b76eb12b33c839649e Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 15 Oct 2019 09:39:49 -0600 Subject: [PATCH 07/31] consolidate testing login into TestCase --- tests/wpacceptance/DistributedPostTest.php | 80 +++++----------------- tests/wpacceptance/includes/TestCase.php | 63 +++++++++++++++++ 2 files changed, 81 insertions(+), 62 deletions(-) diff --git a/tests/wpacceptance/DistributedPostTest.php b/tests/wpacceptance/DistributedPostTest.php index d315ce620..e06c4c772 100644 --- a/tests/wpacceptance/DistributedPostTest.php +++ b/tests/wpacceptance/DistributedPostTest.php @@ -102,79 +102,35 @@ public function testDistributedFrom() { } /** - * Test network push status updates and the `dt_distribute_post_status` filter. + * Test network push status updates with the `dt_distribute_post_status` filter. */ public function testPushStatusDistribution() { - $I = $this->openBrowserPage(); $I->loginAs( 'wpsnapshots' ); - $editor_has_blocks = $this->editorHasBlocks( $I ); // Don't test in block editor. + $editor_has_blocks = $this->editorHasBlocks( $I ); if ( $editor_has_blocks ) { return; } - // TEST SCENARIO: with 'dt_distribute_post_status' FALSE (DEFAULT). - $post_info = $this->pushPost( $I, 40, 2, '', 'Draft' ); - - $I->moveTo( $post_info['distributed_edit_url'] ); - $I->waitUntilElementVisible( 'body.post-php' ); - - $status = $I->getElement( '#post-status-display' ); - $content = trim( $I->getElementInnerText( $status ) ); - - // The remote post should be in a draft state. - $I->seeText( 'Draft', '#post-status-display' ); - - // Publish the remote post - status will be 'published'. - $I->click( '#publish' ); - $I->waitUntilElementVisible( '#wpadminbar' ); - - // Change the origin post status to draft. - $I->moveTo( $post_info['original_edit_url'] ); - $I->waitUntilElementVisible( 'body.post-php' ); - - // The origin post should be in a published state. - $I->seeText( 'Published', '#post-status-display' ); - - // Change the remote post status to Draft. - $I->click( '.edit-post-status' ); - $I->waitUntilElementVisible( '#post_status' ); - $I->selectOptionByValue( '#post_status', 'draft' ); - $I->click( '.save-post-status' ); - - usleep( 300 ); - $I->click( '#publish' ); - $I->waitUntilElementVisible( '#wpadminbar' ); - - // The remote post will still be in a published status, the post status is not distributed. - $I->moveTo( $post_info['distributed_edit_url'] ); - $I->waitUntilElementVisible( 'body.post-php' ); - $I->seeText( 'Published', '#post-status-display' ); - - // TEST SCENARIO: with 'dt_distribute_post_status' true - activate the helper plugin. - $I->moveTo( '/wp-admin/plugins.php' ); - $I->click( '[data-slug="enable-post-status-distribution"] .activate a' ); - $I->waitUntilElementVisible( '#message' ); - - // Update the origin post - $I->moveTo( $post_info['original_edit_url'] ); - $I->waitUntilElementVisible( 'body.post-php' ); - - // The origin post should be in a draft state. - $I->seeText( 'Draft', '#post-status-display' ); - - // Change the remote post title and update. - $I->typeInField( '#title', 'Updated test title' ); - $I->click( '#save-post' ); - $I->waitUntilElementVisible( '#wpadminbar' ); + $post_info = $this->pushPost( $I, 40, 2, '', 'Published' ); + $this->testStatusDistribution( $post_info, $I ); + } + /** + * Test network pull status updates with the `dt_distribute_post_status` filter. + */ + public function testPullStatusDistribution() { + $I = $this->openBrowserPage(); + $I->loginAs( 'wpsnapshots' ); - // The remote post should now in a draft status, the post status is distributed. - $I->moveTo( $post_info['distributed_edit_url'] ); - $I->waitUntilElementVisible( 'body.post-php' ); - $I->seeText( 'Draft', '#post-status-display' ); + // Don't test in block editor. + $editor_has_blocks = $this->editorHasBlocks( $I ); + if ( $editor_has_blocks ) { + return; + } + $post_info = $this->pullPost( $I, 40, 'two', '' ); + $this->testStatusDistribution( $post_info, $I ); } - } diff --git a/tests/wpacceptance/includes/TestCase.php b/tests/wpacceptance/includes/TestCase.php index babb4dd25..acb764241 100644 --- a/tests/wpacceptance/includes/TestCase.php +++ b/tests/wpacceptance/includes/TestCase.php @@ -166,4 +166,67 @@ protected function dismissNUXTip( $actor ) { } catch ( \Exception $e ) {} } + /** + * Test status distribution. + * + * @param Object $post_info Information about the distributed post. + * @param \WPAcceptance\PHPUnit\Actor $actor The Actor instance. + */ + protected function testStatusDistribution( $post_info, $actor ) { + + // TEST SCENARIO: with 'dt_distribute_post_status' false (DEFAULT). + // Deactivate the plugin? + $actor->moveTo( '/wp-admin/plugins.php' ); + usleep( 300 ); + + // Check the distributed post state. + $actor->moveTo( $post_info['distributed_edit_url'] ); + $actor->waitUntilElementVisible( 'body.post-php' ); + + // The remote post should start in a Published post status. + $actor->seeText( 'Published', '#post-status-display' ); + + // Go back to the origin post. + $actor->moveTo( $post_info['original_edit_url'] ); + $actor->waitUntilElementVisible( 'body.post-php' ); + + // The origin post should be in a published post status. + $actor->seeText( 'Published', '#post-status-display' ); + + // Change the origin post status to draft. + $actor->click( '.edit-post-status' ); + $actor->waitUntilElementVisible( '#post_status' ); + $actor->selectOptionByValue( '#post_status', 'draft' ); + $actor->click( '.save-post-status' ); + usleep( 200 ); + $actor->click( '#publish' ); // Click the 'Update' button. + $actor->waitUntilElementVisible( '#wpadminbar' ); + + // The remote post will still be in a published status, the post status is not distributed. + $actor->moveTo( $post_info['distributed_edit_url'] ); + $actor->waitUntilElementVisible( 'body.post-php' ); + $actor->seeText( 'Published', '#post-status-display' ); + + // TEST SCENARIO: with 'dt_distribute_post_status' true - activate the helper plugin. + $actor->moveTo( '/wp-admin/plugins.php' ); + $actor->click( '[data-slug="enable-post-status-distribution"] .activate a' ); + $actor->waitUntilElementVisible( '#message' ); + + // Update the origin post + $actor->moveTo( $post_info['original_edit_url'] ); + $actor->waitUntilElementVisible( 'body.post-php' ); + + // The origin post should be in a draft state. + $actor->seeText( 'Draft', '#post-status-display' ); + + // Change the remote post title and update. + $actor->typeInField( '#title', 'Updated test title' ); + $actor->click( '#save-post' ); + $actor->waitUntilElementVisible( '#wpadminbar' ); + + // The remote post should now in a draft status, the post status is distributed. + $actor->moveTo( $post_info['distributed_edit_url'] ); + $actor->waitUntilElementVisible( 'body.post-php' ); + $actor->seeText( 'Draft', '#post-status-display' ); + } } From 826107f7f66350c2132049fe05f3ddef86ef047f Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 15 Oct 2019 09:48:39 -0600 Subject: [PATCH 08/31] test cleanup --- tests/wpacceptance/DistributedPostTest.php | 6 +++--- tests/wpacceptance/includes/TestCase.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/wpacceptance/DistributedPostTest.php b/tests/wpacceptance/DistributedPostTest.php index e06c4c772..1056298aa 100644 --- a/tests/wpacceptance/DistributedPostTest.php +++ b/tests/wpacceptance/DistributedPostTest.php @@ -114,8 +114,8 @@ public function testPushStatusDistribution() { return; } - $post_info = $this->pushPost( $I, 40, 2, '', 'Published' ); - $this->testStatusDistribution( $post_info, $I ); + $post_info = $this->pushPost( $I, 40, 2 ); + $this->statusDistributionTest( $post_info, $I ); } /** * Test network pull status updates with the `dt_distribute_post_status` filter. @@ -131,6 +131,6 @@ public function testPullStatusDistribution() { } $post_info = $this->pullPost( $I, 40, 'two', '' ); - $this->testStatusDistribution( $post_info, $I ); + $this->statusDistributionTest( $post_info, $I ); } } diff --git a/tests/wpacceptance/includes/TestCase.php b/tests/wpacceptance/includes/TestCase.php index acb764241..38c7ebaae 100644 --- a/tests/wpacceptance/includes/TestCase.php +++ b/tests/wpacceptance/includes/TestCase.php @@ -172,7 +172,7 @@ protected function dismissNUXTip( $actor ) { * @param Object $post_info Information about the distributed post. * @param \WPAcceptance\PHPUnit\Actor $actor The Actor instance. */ - protected function testStatusDistribution( $post_info, $actor ) { + protected function statusDistributionTest( $post_info, $actor ) { // TEST SCENARIO: with 'dt_distribute_post_status' false (DEFAULT). // Deactivate the plugin? From dbaa76b07a87268574ce2ab7481629240e52ad92 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 15 Oct 2019 09:51:52 -0600 Subject: [PATCH 09/31] Complete network tests --- tests/wpacceptance/DistributedPostTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/wpacceptance/DistributedPostTest.php b/tests/wpacceptance/DistributedPostTest.php index 1056298aa..c9ab3dfa2 100644 --- a/tests/wpacceptance/DistributedPostTest.php +++ b/tests/wpacceptance/DistributedPostTest.php @@ -104,7 +104,7 @@ public function testDistributedFrom() { /** * Test network push status updates with the `dt_distribute_post_status` filter. */ - public function testPushStatusDistribution() { + public function testNetworkPushStatusDistribution() { $I = $this->openBrowserPage(); $I->loginAs( 'wpsnapshots' ); @@ -120,7 +120,7 @@ public function testPushStatusDistribution() { /** * Test network pull status updates with the `dt_distribute_post_status` filter. */ - public function testPullStatusDistribution() { + public function testNetworkPullStatusDistribution() { $I = $this->openBrowserPage(); $I->loginAs( 'wpsnapshots' ); From 4731ed7c628f501843a27d8e4acb2b3b14164ebf Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 15 Oct 2019 13:35:06 -0600 Subject: [PATCH 10/31] Add helper for creating an external connection --- tests/wpacceptance/includes/TestCase.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/wpacceptance/includes/TestCase.php b/tests/wpacceptance/includes/TestCase.php index 38c7ebaae..c9b38d42c 100644 --- a/tests/wpacceptance/includes/TestCase.php +++ b/tests/wpacceptance/includes/TestCase.php @@ -229,4 +229,19 @@ protected function statusDistributionTest( $post_info, $actor ) { $actor->waitUntilElementVisible( 'body.post-php' ); $actor->seeText( 'Draft', '#post-status-display' ); } + + /** + * Create an external connection. + * + * @param \WPAcceptance\PHPUnit\Actor $actor The Actor instance. + */ + protected function createExternalConnection( $actor ) { + $actor->moveTo( 'wp-admin/post-new.php?post_type=dt_ext_connection' ); + $actor->typeInField( '#title', 'Test External Connection' ); + $actor->typeInField( '#dt_username', 'wpsnapshots' ); + $actor->typeInField( '#dt_password', 'password' ); + $actor->typeInField( '#dt_external_connection_url', $this->getWPHomeUrl() . '/two/wp-json' ); + $actor->pressEnterKey( '#create-connection' ); + $actor->waitUntilElementVisible( '.notice-success' ); + } } From a0f6ddb0edab545ff3a8867fb480eac4ecfae126 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 15 Oct 2019 13:35:45 -0600 Subject: [PATCH 11/31] Add tests for testExternalPushStatusDistribution --- tests/wpacceptance/DistributedPostTest.php | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/wpacceptance/DistributedPostTest.php b/tests/wpacceptance/DistributedPostTest.php index c9ab3dfa2..06e4cad80 100644 --- a/tests/wpacceptance/DistributedPostTest.php +++ b/tests/wpacceptance/DistributedPostTest.php @@ -133,4 +133,39 @@ public function testNetworkPullStatusDistribution() { $post_info = $this->pullPost( $I, 40, 'two', '' ); $this->statusDistributionTest( $post_info, $I ); } + + /** + * Test external push status updates with the `dt_distribute_post_status` filter. + */ + public function testExternalPushStatusDistribution() { + $I = $this->openBrowserPage(); + $I->loginAs( 'wpsnapshots' ); + + // Don't test in block editor. + $editor_has_blocks = $this->editorHasBlocks( $I ); + if ( $editor_has_blocks ) { + return; + } + + // Create an external connection. + $this->createExternalConnection( $I ); + $url = $I->getCurrentUrl(); + preg_match( '/post=(\d+)/', $url, $matches ); + + $post_info = $this->pushPost( $I, 40, (int) $matches[1], '', 'publish', true ); + $I->moveTo( 'two/wp-admin/edit.php' ); + + // Switch to the distributed post. + $I->waitUntilElementVisible( '#the-list' ); + $I->click( 'a.row-title' ); + $I->waitUntilNavigation(); + + // Use moveTo to prime page object. + $url = $I->getCurrentUrl(); + + $post_info['distributed_edit_url'] = $url; + error_log( json_encode( $post_info, JSON_PRETTY_PRINT ) ); + + $this->statusDistributionTest( $post_info, $I ); + } } From a0ef1188e776ef19a810d8e3b5b9d4cc2f7d4dae Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 15 Oct 2019 13:35:54 -0600 Subject: [PATCH 12/31] Add test for testExternalPullStatusDistribution --- tests/wpacceptance/DistributedPostTest.php | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/wpacceptance/DistributedPostTest.php b/tests/wpacceptance/DistributedPostTest.php index 06e4cad80..0af6b57b0 100644 --- a/tests/wpacceptance/DistributedPostTest.php +++ b/tests/wpacceptance/DistributedPostTest.php @@ -168,4 +168,25 @@ public function testExternalPushStatusDistribution() { $this->statusDistributionTest( $post_info, $I ); } + + /** + * Test external pull status updates with the `dt_distribute_post_status` filter. + */ + public function testExternalPullStatusDistribution() { + $I = $this->openBrowserPage(); + $I->loginAs( 'wpsnapshots' ); + + // Don't test in block editor. + $editor_has_blocks = $this->editorHasBlocks( $I ); + if ( $editor_has_blocks ) { + return; + } + + // Create an external connection. + $this->createExternalConnection( $I ); + // Pull post from external connection. + $post_info = $this->pullPost( $I, 48, 'two', '', 'Test External Connection' ); + $this->statusDistributionTest( $post_info, $I ); + } + } From 8716047991600442710ad200651a705abe1ff4b6 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 15 Oct 2019 14:30:30 -0600 Subject: [PATCH 13/31] when a subscription update includes status, update the post status --- .../classes/API/SubscriptionsController.php | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/includes/classes/API/SubscriptionsController.php b/includes/classes/API/SubscriptionsController.php index 237b7896d..31e670338 100644 --- a/includes/classes/API/SubscriptionsController.php +++ b/includes/classes/API/SubscriptionsController.php @@ -257,15 +257,19 @@ public function receive_item( $request ) { return $response; } - wp_update_post( - [ - 'ID' => $request['post_id'], - 'post_title' => $request['post_data']['title'], - 'post_content' => $content, - 'post_excerpt' => $request['post_data']['excerpt'], - 'post_name' => $request['post_data']['slug'], - ] - ); + $post_update = [ + 'ID' => $request['post_id'], + 'post_title' => $request['post_data']['title'], + 'post_content' => $content, + 'post_excerpt' => $request['post_data']['excerpt'], + 'post_name' => $request['post_data']['slug'], + ]; + + if ( isset( $request['post_data']['status'] ) ) { + $post_update['post_status'] = $request['post_data']['status']; + } + + wp_update_post( $post_update ); /** * We check if each of these exist since the API removes empty arrays from requests From e0d36cafdf23753761ccb18433a5875eb3867df3 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 15 Oct 2019 14:30:43 -0600 Subject: [PATCH 14/31] cleanup --- tests/wpacceptance/DistributedPostTest.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/wpacceptance/DistributedPostTest.php b/tests/wpacceptance/DistributedPostTest.php index 0af6b57b0..a1c24ec82 100644 --- a/tests/wpacceptance/DistributedPostTest.php +++ b/tests/wpacceptance/DistributedPostTest.php @@ -155,16 +155,12 @@ public function testExternalPushStatusDistribution() { $post_info = $this->pushPost( $I, 40, (int) $matches[1], '', 'publish', true ); $I->moveTo( 'two/wp-admin/edit.php' ); - // Switch to the distributed post. + // Grab the distributed post edit URL. $I->waitUntilElementVisible( '#the-list' ); $I->click( 'a.row-title' ); $I->waitUntilNavigation(); - - // Use moveTo to prime page object. $url = $I->getCurrentUrl(); - $post_info['distributed_edit_url'] = $url; - error_log( json_encode( $post_info, JSON_PRETTY_PRINT ) ); $this->statusDistributionTest( $post_info, $I ); } From 0817a9991c5c310bc21355c7895f908ae8dc0c76 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 15 Oct 2019 14:31:04 -0600 Subject: [PATCH 15/31] Subscriptions: respect dt_distribute_post_status filter --- includes/subscriptions.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/includes/subscriptions.php b/includes/subscriptions.php index 024792604..bf84acecd 100644 --- a/includes/subscriptions.php +++ b/includes/subscriptions.php @@ -277,6 +277,16 @@ function send_notifications( $post_id ) { } } + /** + * Filter whether Distributor should update post statuses when the origin post status changes. + * + * False by default, return true to have post statuses distributed. + */ + $distribute_post_status = apply_filters( 'dt_distribute_post_status', false ); + if ( $distribute_post_status ) { + $post_body['post_data']['status'] = $post->post_status; + } + $request = wp_remote_post( untrailingslashit( $target_url ) . '/wp/v2/dt_subscription/receive', [ From 795aaa4019c74715061dfaedf72c925a9ffd03ca Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 15 Oct 2019 14:32:01 -0600 Subject: [PATCH 16/31] external push: initial push can stay as is - status is controlled in menu --- .../ExternalConnections/WordPressExternalConnection.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/includes/classes/ExternalConnections/WordPressExternalConnection.php b/includes/classes/ExternalConnections/WordPressExternalConnection.php index b84a23887..bfe3921f9 100644 --- a/includes/classes/ExternalConnections/WordPressExternalConnection.php +++ b/includes/classes/ExternalConnections/WordPressExternalConnection.php @@ -548,9 +548,6 @@ public function push( $post_id, $args = array() ) { $signature = \Distributor\Subscriptions\generate_signature(); - $distribute_post_status = apply_filters( 'dt_distribute_post_status', false ); - $distribute_status = $distribute_post_status ? $post->post_status : 'publish'; - /** * Now let's push */ @@ -559,7 +556,7 @@ public function push( $post_id, $args = array() ) { 'slug' => $post->post_name, 'content' => Utils\get_processed_content( $post->post_content ), 'type' => $post->post_type, - 'status' => ( ! empty( $args['post_status'] ) ) ? $args['post_status'] : $distribute_status, + 'status' => ( ( ! empty( $args['post_status'] ) ) ? $args['post_status'] : 'publish' ), 'excerpt' => $post->post_excerpt, 'distributor_original_source_id' => $this->id, 'distributor_original_site_name' => get_bloginfo( 'name' ), From 1a2fe66cc23ed70e2784059174fb2a6dcfd8df29 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 15 Oct 2019 14:35:04 -0600 Subject: [PATCH 17/31] cleanup --- .../classes/ExternalConnections/WordPressExternalConnection.php | 2 +- tests/wpacceptance/DistributedPostTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/classes/ExternalConnections/WordPressExternalConnection.php b/includes/classes/ExternalConnections/WordPressExternalConnection.php index bfe3921f9..3935c985a 100644 --- a/includes/classes/ExternalConnections/WordPressExternalConnection.php +++ b/includes/classes/ExternalConnections/WordPressExternalConnection.php @@ -556,7 +556,7 @@ public function push( $post_id, $args = array() ) { 'slug' => $post->post_name, 'content' => Utils\get_processed_content( $post->post_content ), 'type' => $post->post_type, - 'status' => ( ( ! empty( $args['post_status'] ) ) ? $args['post_status'] : 'publish' ), + 'status' => ( ! empty( $args['post_status'] ) ) ? $args['post_status'] : 'publish', 'excerpt' => $post->post_excerpt, 'distributor_original_source_id' => $this->id, 'distributor_original_site_name' => get_bloginfo( 'name' ), diff --git a/tests/wpacceptance/DistributedPostTest.php b/tests/wpacceptance/DistributedPostTest.php index a1c24ec82..7db466c65 100644 --- a/tests/wpacceptance/DistributedPostTest.php +++ b/tests/wpacceptance/DistributedPostTest.php @@ -181,7 +181,7 @@ public function testExternalPullStatusDistribution() { // Create an external connection. $this->createExternalConnection( $I ); // Pull post from external connection. - $post_info = $this->pullPost( $I, 48, 'two', '', 'Test External Connection' ); + $post_info = $this->pullPost( $I, 40, 'two', '', 'Test External Connection' ); $this->statusDistributionTest( $post_info, $I ); } From 85446c25d3c5c17abb6b516f71073c76de0a43b8 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 15 Oct 2019 14:41:19 -0600 Subject: [PATCH 18/31] Add @hook notation --- .../classes/InternalConnections/NetworkSiteConnection.php | 6 +++++- includes/subscriptions.php | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/includes/classes/InternalConnections/NetworkSiteConnection.php b/includes/classes/InternalConnections/NetworkSiteConnection.php index cef1b816b..ed220ec45 100644 --- a/includes/classes/InternalConnections/NetworkSiteConnection.php +++ b/includes/classes/InternalConnections/NetworkSiteConnection.php @@ -72,7 +72,11 @@ public function push( $post_id, $args = array() ) { * Filter whether Distributor should update post statuses when the origin post status changes. * * False by default, return true to have post statuses distributed. - */ + * + * @since 2.0.0 + * @hook dt_distribute_post_status + * + **/ $distribute_post_status = apply_filters( 'dt_distribute_post_status', false ); $new_post_args = array( diff --git a/includes/subscriptions.php b/includes/subscriptions.php index bf84acecd..859286280 100644 --- a/includes/subscriptions.php +++ b/includes/subscriptions.php @@ -281,6 +281,9 @@ function send_notifications( $post_id ) { * Filter whether Distributor should update post statuses when the origin post status changes. * * False by default, return true to have post statuses distributed. + * + * @since 2.0.0 + * @hook dt_distribute_post_status */ $distribute_post_status = apply_filters( 'dt_distribute_post_status', false ); if ( $distribute_post_status ) { From e1ffa7f1f9eee47843b616cc0717f11247c24dc8 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 15 Oct 2019 15:39:29 -0600 Subject: [PATCH 19/31] fixes from phpcbf --- includes/classes/InternalConnections/NetworkSiteConnection.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/includes/classes/InternalConnections/NetworkSiteConnection.php b/includes/classes/InternalConnections/NetworkSiteConnection.php index ed220ec45..78046fc6a 100644 --- a/includes/classes/InternalConnections/NetworkSiteConnection.php +++ b/includes/classes/InternalConnections/NetworkSiteConnection.php @@ -75,8 +75,7 @@ public function push( $post_id, $args = array() ) { * * @since 2.0.0 * @hook dt_distribute_post_status - * - **/ + */ $distribute_post_status = apply_filters( 'dt_distribute_post_status', false ); $new_post_args = array( From 9c95b15d379a12d88de7208c00abeeaddd7eb14f Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Wed, 16 Oct 2019 07:28:43 -0600 Subject: [PATCH 20/31] =?UTF-8?q?Don=E2=80=99t=20run=20testDistributedCoun?= =?UTF-8?q?t=20in=20block=20editor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/wpacceptance/DistributedPostTest.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/wpacceptance/DistributedPostTest.php b/tests/wpacceptance/DistributedPostTest.php index 7db466c65..babfa8d15 100644 --- a/tests/wpacceptance/DistributedPostTest.php +++ b/tests/wpacceptance/DistributedPostTest.php @@ -19,6 +19,12 @@ public function testDistributedCount() { $I->loginAs( 'wpsnapshots' ); + // Don't test in block editor. + $editor_has_blocks = $this->editorHasBlocks( $I ); + if ( $editor_has_blocks ) { + return; + } + self::assertPostFieldContains( 40, 'post_title', 'Test Post' ); // Distribute post From 32a9cfa2f8a176e1624763ebfc54f1dc2eef0753 Mon Sep 17 00:00:00 2001 From: Tung Du Date: Mon, 10 Feb 2020 08:32:20 +0700 Subject: [PATCH 21/31] refactor(internal-connections): reduce indentation level --- .../InternalConnections/NetworkSiteConnection.php | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/includes/classes/InternalConnections/NetworkSiteConnection.php b/includes/classes/InternalConnections/NetworkSiteConnection.php index 78046fc6a..4aaa9c002 100644 --- a/includes/classes/InternalConnections/NetworkSiteConnection.php +++ b/includes/classes/InternalConnections/NetworkSiteConnection.php @@ -108,15 +108,11 @@ public function push( $post_id, $args = array() ) { } if ( empty( $args['post_status'] ) ) { - if ( isset( $new_post_args['ID'] ) ) { - - if ( ! $distribute_post_status ) { - - // Avoid updating the status of previously distributed posts. - $existing_status = get_post_status( (int) $new_post_args['ID'] ); - if ( $existing_status ) { - $new_post_args['post_status'] = $existing_status; - } + if ( isset( $new_post_args['ID'] ) && ! $distribute_post_status ) { + // Avoid updating the status of previously distributed posts. + $existing_status = get_post_status( (int) $new_post_args['ID'] ); + if ( $existing_status ) { + $new_post_args['post_status'] = $existing_status; } } } else { From 4dabefd20315459d7183359c0554ba28822cf2be Mon Sep 17 00:00:00 2001 From: Tung Du Date: Mon, 10 Feb 2020 10:21:47 +0700 Subject: [PATCH 22/31] refactor: wrap `dt_distribute_post_status` filter in a helper function --- .../NetworkSiteConnection.php | 14 ++------------ includes/subscriptions.php | 11 +---------- includes/utils.php | 17 +++++++++++++++++ 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/includes/classes/InternalConnections/NetworkSiteConnection.php b/includes/classes/InternalConnections/NetworkSiteConnection.php index 4aaa9c002..eee1a4539 100644 --- a/includes/classes/InternalConnections/NetworkSiteConnection.php +++ b/includes/classes/InternalConnections/NetworkSiteConnection.php @@ -68,16 +68,6 @@ public function push( $post_id, $args = array() ) { $original_post_url = get_permalink( $post_id ); $using_gutenberg = \Distributor\Utils\is_using_gutenberg( $post ); - /** - * Filter whether Distributor should update post statuses when the origin post status changes. - * - * False by default, return true to have post statuses distributed. - * - * @since 2.0.0 - * @hook dt_distribute_post_status - */ - $distribute_post_status = apply_filters( 'dt_distribute_post_status', false ); - $new_post_args = array( 'post_title' => get_the_title( $post_id ), 'post_name' => $post->post_name, @@ -85,7 +75,7 @@ public function push( $post_id, $args = array() ) { 'post_excerpt' => $post->post_excerpt, 'post_type' => $post->post_type, 'post_author' => get_current_user_id(), - 'post_status' => $distribute_post_status ? $post->post_status : 'publish', + 'post_status' => Utils\can_distribute_post_status() ? $post->post_status : 'publish', ); $media = \Distributor\Utils\prepare_media( $post_id ); @@ -108,7 +98,7 @@ public function push( $post_id, $args = array() ) { } if ( empty( $args['post_status'] ) ) { - if ( isset( $new_post_args['ID'] ) && ! $distribute_post_status ) { + if ( isset( $new_post_args['ID'] ) && ! Utils\can_distribute_post_status() ) { // Avoid updating the status of previously distributed posts. $existing_status = get_post_status( (int) $new_post_args['ID'] ); if ( $existing_status ) { diff --git a/includes/subscriptions.php b/includes/subscriptions.php index 859286280..ce72d5394 100644 --- a/includes/subscriptions.php +++ b/includes/subscriptions.php @@ -277,16 +277,7 @@ function send_notifications( $post_id ) { } } - /** - * Filter whether Distributor should update post statuses when the origin post status changes. - * - * False by default, return true to have post statuses distributed. - * - * @since 2.0.0 - * @hook dt_distribute_post_status - */ - $distribute_post_status = apply_filters( 'dt_distribute_post_status', false ); - if ( $distribute_post_status ) { + if ( Utils\can_distribute_post_status() ) { $post_body['post_data']['status'] = $post->post_status; } diff --git a/includes/utils.php b/includes/utils.php index a5df35252..79f919039 100644 --- a/includes/utils.php +++ b/includes/utils.php @@ -800,3 +800,20 @@ function get_processed_content( $post_content ) { return $post_content; } + +/** + * Check if post status can be distributed. + * + * @return bool + */ +function can_distribute_post_status() { + /** + * Filter whether Distributor should update post statuses when the origin post status changes. + * + * False by default, return true to have post statuses distributed. + * + * @since 2.0.0 + * @hook dt_distribute_post_status + */ + return apply_filters( 'dt_distribute_post_status', false ); +} From c6a89c6c37d42b7377a36083e2b66aefddb59222 Mon Sep 17 00:00:00 2001 From: Tung Du Date: Tue, 25 Feb 2020 10:57:39 +0700 Subject: [PATCH 23/31] fix: merge conflict --- .babelrc | 2 +- .github/ISSUE_TEMPLATE/1-bug-report.md | 2 +- .github/PULL_REQUEST_TEMPLATE.md | 4 + .github/hookdoc-tmpl/README.md | 9 + .github/hookdoc-tmpl/layout.tmpl | 47 + .github/hookdoc-tmpl/static/styles-10up.css | 98 + .github/workflows/build-docs.yml | 28 + README.md | 44 +- assets/css/push.css | 18 +- assets/js/admin-external-connection.js | 32 +- assets/js/admin-pull.js | 10 +- assets/js/push.js | 181 +- composer.json | 3 +- composer.lock | 31 +- distributor.php | 4 +- gulp-tasks/cssclean.js | 2 +- gulp-tasks/csscomplete.js | 4 +- gulp-tasks/cssnano.js | 18 +- gulp-tasks/jsclean.js | 4 +- gulp-tasks/webpack.js | 3 +- gulpfile.babel.js | 30 +- hookdoc-conf.json | 18 +- includes/.github/workflows/build-docs.yml | 29 - .../classes/API/SubscriptionsController.php | 4 +- includes/classes/Authentication.php | 18 +- .../Authentications/WordPressBasicAuth.php | 8 +- .../WordPressDotcomOauth2Authentication.php | 7 +- includes/classes/ExternalConnection.php | 6 +- .../WordPressExternalConnection.php | 93 +- .../NetworkSiteConnection.php | 86 +- includes/classes/PullListTable.php | 15 +- includes/external-connection-cpt.php | 8 +- includes/pull-ui.php | 4 +- includes/push-ui.php | 37 +- includes/rest-api.php | 12 +- includes/subscriptions.php | 6 +- includes/syndicated-post-ui.php | 15 +- includes/template-tags.php | 4 +- includes/utils.php | 124 +- package-lock.json | 10784 +++++++++------- package.json | 56 +- webpack.config.babel.js | 20 +- 42 files changed, 6855 insertions(+), 5073 deletions(-) create mode 100644 .github/hookdoc-tmpl/README.md create mode 100644 .github/hookdoc-tmpl/layout.tmpl create mode 100644 .github/hookdoc-tmpl/static/styles-10up.css create mode 100644 .github/workflows/build-docs.yml delete mode 100644 includes/.github/workflows/build-docs.yml diff --git a/.babelrc b/.babelrc index 28fc5d3c9..54e093732 100644 --- a/.babelrc +++ b/.babelrc @@ -1,3 +1,3 @@ { - "presets" : ["env", "react"] + "presets" : ["@babel/preset-env", "@babel/preset-react"] } diff --git a/.github/ISSUE_TEMPLATE/1-bug-report.md b/.github/ISSUE_TEMPLATE/1-bug-report.md index 5c69593ca..84e340c8a 100644 --- a/.github/ISSUE_TEMPLATE/1-bug-report.md +++ b/.github/ISSUE_TEMPLATE/1-bug-report.md @@ -7,7 +7,7 @@ assignees: '' --- - + **Describe the bug** diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index d88817207..589e78098 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -50,3 +50,7 @@ Describe the actions you performed (e.g., commands you ran, text you typed, butt ### Applicable Issues + +### Changelog Entry + + diff --git a/.github/hookdoc-tmpl/README.md b/.github/hookdoc-tmpl/README.md new file mode 100644 index 000000000..7044e6689 --- /dev/null +++ b/.github/hookdoc-tmpl/README.md @@ -0,0 +1,9 @@ +# Welcome to the Distributor Plugin Hook Documentation + +This resource is generated documentation on actions and filters found in the Distributor plugin. Use the sidebar to browse and navigate. + +For more information about using Distributor with WordPress, please see the [Distributor website](https://distributorplugin.com/). + +To report an issue with Distributor or contribute back to the project, please visit the [GitHub repository](https://github.com/10up/distributor/). + + diff --git a/.github/hookdoc-tmpl/layout.tmpl b/.github/hookdoc-tmpl/layout.tmpl new file mode 100644 index 000000000..db241bd4d --- /dev/null +++ b/.github/hookdoc-tmpl/layout.tmpl @@ -0,0 +1,47 @@ + + + + + <?js= title ?> - 10up Distributor Hook Docs + + + + + + + + + + + + class="home"> + +
+ + +

+ + + + + + + +
+ + + +
+ + + + + diff --git a/.github/hookdoc-tmpl/static/styles-10up.css b/.github/hookdoc-tmpl/static/styles-10up.css new file mode 100644 index 000000000..e477c3b4e --- /dev/null +++ b/.github/hookdoc-tmpl/static/styles-10up.css @@ -0,0 +1,98 @@ +body { + background: #fefefe; + color: #232323; + font-family: 'IBM Plex Sans', sans-serif; + font-size: 1.3rem; + font-weight: 300; +} + +h1, h2, h3 { + line-height: 1.2; + font-family: 'Playfair Display', sans-serif; + font-weight: 900; + letter-spacing: -.01em; +} + +h1.page-title { + font-size: 42px; + margin-top: .5em; +} + +nav ul { + font-size: 1.2rem; +} + +nav li a { + background-image: none; +} + +nav li a:hover { + text-decoration: underline; +} + +code, pre, +nav ul a, nav ul a:visited, nav ul a:active, +.name, .signature, +.params .name, .props .name, +.name code { + font-family: 'IBM Plex Mono', monospace; +} + +article h1 { + margin: 12px 0 32px; +} + +a { + background-image: linear-gradient(transparent calc(100% - 7px), #f2dede 0), + linear-gradient(transparent calc(100% - 7px), #cef8f7 0); + background-position: 0 0; + background-repeat: no-repeat; + background-size: 0 100%, 100% 100%; + color: #232323; + text-decoration: none; + transition: all .1s; +} + +a:visited, +a:active { + color: #232323; +} + +a:focus, +a:hover { + background-size: 100% 100%, 100% 100%; + color: #232323; + text-decoration: none; +} + +a.banner { + background-image: none; + margin-left: -10px; +} + +a.banner img { + width: 100%; + max-width: 888px; +} + +footer { + text-align: center; + font-size: .8em; + font-style: normal; + font-weight: 300; +} + +.home #main > section:first-of-type, +.home nav > h2 { + display: none; +} + +.prettyprint.source { + font-size: 14px; +} + +.prettyprint code { + padding: 2px 10px; + line-height: 16px; + height: 16px; +} diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml new file mode 100644 index 000000000..2e66323f2 --- /dev/null +++ b/.github/workflows/build-docs.yml @@ -0,0 +1,28 @@ +name: Build Hook Docs + +on: + push: + branches: + - develop + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Use Node.js 10 + uses: actions/setup-node@v1 + with: + node-version: '10.x' + - name: npm install, and build docs + run: | + npm install + npm -g install gulp-cli + npm run build:docs + env: + CI: true + - name: Deploy to GH Pages + uses: maxheld83/ghpages@v0.2.1 + env: + BUILD_DIR: 'docs/' + GH_PAT: ${{ secrets.GH_PAT }} diff --git a/README.md b/README.md index d66b8d226..b9cd69dbf 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,13 @@ > Distributor is a WordPress plugin that makes it easy to syndicate and reuse content across your websites — whether in a single multisite or across the web. [![Support Level](https://img.shields.io/badge/support-active-green.svg)](#support-level) [![Build Status](https://travis-ci.org/10up/distributor.svg?branch=master)](https://travis-ci.org/10up/distributor) -[![Release Version](https://img.shields.io/github/release/10up/distributor.svg)](https://github.com/10up/distributor/releases/latest) ![WordPress tested up to version](https://img.shields.io/badge/WordPress-v5.2%20tested-success.svg) [![License](https://img.shields.io/github/license/10up/distributor.svg)](https://github.com/10up/distributor/blob/develop/LICENSE.md) +[![Release Version](https://img.shields.io/github/release/10up/distributor.svg)](https://github.com/10up/distributor/releases/latest) ![WordPress tested up to version](https://img.shields.io/badge/WordPress-v5.3%20tested-success.svg) [![License](https://img.shields.io/github/license/10up/distributor.svg)](https://github.com/10up/distributor/blob/develop/LICENSE.md) *You can learn more about Distributor's features at [DistributorPlugin.com](https://distributorplugin.com).* _Note:_ The latest stable version of the plugin is the _stable_ branch. [Download the stable branch]((https://github.com/10up/distributor/archive/stable.zip)) if you are intending to use the plugin in production. -## Table of Contents +## Table of Contents * [Features](#features) * [Gutenberg Support](#gutenberg-support-beta) * [Requirements](#requirements) @@ -18,10 +18,12 @@ _Note:_ The latest stable version of the plugin is the _stable_ branch. [Downloa * [Registration](#registration) * [Setup External Connections](#setup-external-connections-using-application-passwords) * [Known Caveats/Issues](#known-caveatsissues) +* [Developers](#developers) + * [Running Locally](#running-locally) + * [Testing](#testing) + * [Debugging](#debugging) * [Changelog](#changelog) * [Contributing](#contributing) -* [Testing](#testing) -* [Debugging](#debugging) ## Features @@ -38,12 +40,16 @@ Content this is distributed (via Push or Pull) is connected to the original. Re There are two connection types: `internal` and `external`. * Internal connections are other sites inside of the same multisite network. Any user logged into the network can distribute any content in the network to any other sites in the network where that user has permission to publish posts (assuming the site supports the same post type). -* External connections are external websites, connected by the JSON REST API. External connections can be added in the WordPress admin dashboard under Distributor > External Connections. Administrators can decide which user roles are allowed to distribute content to and from that connection (Editors and Administrators by default). All users with those roles will inherit the permissions of the user account used to establish the remote connection. +* External connections are external websites, connected by the JSON REST API. External connections can be added in the WordPress admin dashboard under `Distributor` > `External Connections`. Administrators can decide which user roles are allowed to distribute content to and from that connection (Editors and Administrators by default). All users with those roles will inherit the permissions of the user account used to establish the remote connection. ### Gutenberg Support (Beta) Distributor supports distributing Gutenberg posts but the functionality is currently in beta. We expect this functionality to stabilize as Gutenberg further iterates within WordPress core. +### Extendability + +Distributor is built with the same extensible approach as WordPress itself, with [fully documented hooks and filters](https://10up.github.io/distributor/) to customize its default behavior and create custom distribution workflows. You can even create connections to other platforms. + ## Requirements * PHP 5.6+ @@ -55,7 +61,7 @@ Distributor supports distributing Gutenberg posts but the functionality is curre For production use, we recommend [registering and downloading the plugin from DistributorPlugin.com](https://distributorplugin.com/#cta) – it's 100% free. You will be emailed a direct link to download the latest, production-ready build. Alternatively, you can [download the latest master build from GitHub](https://github.com/10up/distributor/archive/master.zip). -You can upload and install the archived (zip) plugin via the WordPress dashboard (Plugins > Add New -> Upload Plugin) or manually inside of the `wp-content/plugins` directory, and activate on the Plugins dashboard. +You can upload and install the archived (zip) plugin via the WordPress dashboard (`Plugins` > `Add New` -> `Upload Plugin`) or manually inside of the `wp-content/plugins` directory, and activate on the Plugins dashboard. ### Registration @@ -66,7 +72,7 @@ To help inform our roadmap, keep adopters apprised of major updates and changes 1. Ensure Distributor is installed on BOTH sites being connected. We'll refer to these as mainsite.com and remotesite.com. 2. On mainsite.com, navigate to `Distributor` > `External Connections` and click `Add New`. 3. Enter a label for the connection (e.g., `remotesite.com`), select `Username / Password` for the `Authentication Method`, and a username from remotesite.com. -4. On remotesite.com, ensure that [Application Passwords](https://wordpress.org/plugins/application-passwords/) is installed. Then navigate to the user profile that will be used to create the Externatal Connection on mainsite.com and then to the `Application Passwords` section of the user profile (not the `Account Management` section). Add a label for the New Application Password Name (e.g., `mainsite.com`) and click `Add New`. Now copy the password provided into mainsite.com's External Connections `Password` field. +4. On remotesite.com, ensure that [Application Passwords](https://wordpress.org/plugins/application-passwords/) is installed. Then navigate to the user profile that will be used to create the External Connection on mainsite.com and then to the `Application Passwords` section of the user profile (not the `Account Management` section). Add a label for the New Application Password Name (e.g., `mainsite.com`) and click `Add New`. Now copy the password provided into mainsite.com's External Connections `Password` field. 5. On mainsite.com, add the `External Connection URL` (e.g., http://remotesite.com/wp-json). You should see a green circle and "_Connection established._". 6. Ensure the roles selected in `Roles Allowed to Push` are the ones you want to support, then press the `Create Connection` button. You should now be able to push from mainsite.com to remotesite.com. If you want to pull from remotesite.com to mainsite.com, simply repeat these instructions swapping mainsite.com and remotesite.com. @@ -76,9 +82,9 @@ To help inform our roadmap, keep adopters apprised of major updates and changes ## Known Caveats/Issues -__Remote Request Timeouts__ - With external connections, HTTP requests are sent back and forth - creating posts, transfering images, syncing post updates, etc. In certain situations, mostly commonly when distributing posts with a large number of images (or very large file sizes), using poorly configured or saturated servers / hosts, or using plugins that add significant weight to post creation, Distributor requests can fail. Although we do some error handling, there are certain cases in which post distribution can fail silently. If distribution requests are taking a long time to load and/or failing, consider adjusting the timeout; you can filter the timeout for pushing external posts [here](https://github.com/10up/distributor/blob/master/includes/classes/ExternalConnections/WordPressExternalConnection.php#L487). More advanced handling of large content requests, and improved error handling is on the road map for a future update. +__Remote Request Timeouts__ - With external connections, HTTP requests are sent back and forth - creating posts, transfering images, syncing post updates, etc. In certain situations, mostly commonly when distributing posts with a large number of images (or very large file sizes), using poorly configured or saturated servers / hosts, or using plugins that add significant weight to post creation, Distributor requests can fail. Although we do some error handling, there are certain cases in which post distribution can fail silently. If distribution requests are taking a long time to load and/or failing, consider adjusting the timeout; you can filter the timeout for pushing external posts using the [`dt_push_post_timeout` filter](https://10up.github.io/distributor/dt_push_post_timeout.html). More advanced handling of large content requests, and improved error handling is on the road map for a future update. -__Post Meta Associations__ - A distributed post includes all of the post meta from the original version. Sometimes arbitrary post meta references an ID for another piece of content on the original site. Distributor _does not_ "bring along" the referenced content or update references for arbitrary post meta (it will take care of updating references in the case of core WordPress features, such as the featured image ID). This issue is very common when using field management plugins like Advanced Custom Fields (ACF). This can be addressed on a case by case basis by extending the plugin; for external connections, you can manually handle post meta associations using [this hook](https://github.com/10up/distributor/blob/master/includes/classes/ExternalConnections/WordPressExternalConnection.php#L512). For internal connections, use [this hook](https://github.com/10up/distributor/blob/master/includes/classes/InternalConnections/NetworkSiteConnection.php#L102). +__Post Meta Associations__ - A distributed post includes all of the post meta from the original version. Sometimes arbitrary post meta references an ID for another piece of content on the original site. Distributor _does not_ "bring along" the referenced content or update references for arbitrary post meta (it will take care of updating references in the case of core WordPress features, such as the featured image ID). This issue is very common when using field management plugins like Advanced Custom Fields (ACF). This can be addressed on a case by case basis by extending the plugin; for external connections, you can manually handle post meta associations using [the `dt_push_post` hook](https://github.com/10up/distributor/blob/master/includes/classes/ExternalConnections/WordPressExternalConnection.php#L646). For internal connections, use the [`dt_push_post` hook](https://10up.github.io/distributor/dt_push_post.html). Note that while named the same, these hooks accept different parameters. __Deleting Distributed Posts__ - When a post that has been distributed is deleted, the distributed copies will become unlinked ("forked") from the original and thus become editable. Similarly, when a distributed post is unpublished, distributed copies will not be unpublished. More sophisticated "removal" workflow is on the road map for a future update. @@ -100,24 +106,30 @@ __Distributing Canonical URL__ - By default, canonical URL of distributed post w __Drafts as preferred Status__ - By default, drafts are the preferred status and can't be changed at the source site. -## Changelog - -A complete listing of all notable changes to Distributor are documented in [CHANGELOG.md](https://github.com/10up/distributor/blob/develop/CHANGELOG.md). +## Developers -## Contributing +### Running Locally -Please read [CODE_OF_CONDUCT.md](https://github.com/10up/distributor/blob/develop/CODE_OF_CONDUCT.md) for details on our code of conduct and [CONTRIBUTING.md](https://github.com/10up/distributor/blob/develop/CONTRIBUTING.md) for details on the process for submitting pull requests to us. +If you are compiling Distributor locally, note that there is a minimum requirement of Node.js 8.10. If you're using an older version of Node, then it will not compile correctly. -## Testing +### Testing The plugin contains a standard test suite compatible with PHPUnit. If you want to test across multiple PHP versions, a [Dockunit](https://github.com/dockunit/dockunit) file is included. -## Debugging +### Debugging You can define a constant `DISTRIBUTOR_DEBUG` to `true` to increase the ease of debugging in Distributor. This will make all remote requests blocking and expose the subscription post type. Enabling this will also provide more debugging information in your error log for image side loading issues. The specific logging method may change in the future. +## Changelog + +A complete listing of all notable changes to Distributor are documented in [CHANGELOG.md](https://github.com/10up/distributor/blob/develop/CHANGELOG.md). + +## Contributing + +Please read [CODE_OF_CONDUCT.md](https://github.com/10up/distributor/blob/develop/CODE_OF_CONDUCT.md) for details on our code of conduct and [CONTRIBUTING.md](https://github.com/10up/distributor/blob/develop/CONTRIBUTING.md) for details on the process for submitting pull requests to us. + ## Like what you see? diff --git a/assets/css/push.css b/assets/css/push.css index 97236b54b..0a13d5275 100644 --- a/assets/css/push.css +++ b/assets/css/push.css @@ -60,6 +60,8 @@ #wpadminbar #distributor-push-wrapper .selected-connections-list { margin-bottom: 1em; + overflow: auto; + max-height: 145px; } #wpadminbar #distributor-push-wrapper .connections-selected { @@ -82,7 +84,10 @@ display: none; } } - +#wpadminbar #distributor-push-wrapper .selectall-connections.unavailable, +#wpadminbar #distributor-push-wrapper .selectno-connections.unavailable { + opacity: .5; +} #wpadminbar #distributor-push-wrapper .connections-selected label { color: inherit; } @@ -154,9 +159,9 @@ #wpadminbar #distributor-push-wrapper input[type=text] { font-size: inherit; padding: 0.5278em; - width: 100%; + width: 96%; background-color: var(--backgroundColor); - border: 2px solid var(--borderColor); + border: 3px solid var(--borderColor); color: var(--fontColor); margin-bottom: .5em; @@ -197,6 +202,9 @@ outline: none; } } +#wpadminbar #distributor-push-wrapper .selectall-connections { + margin-bottom: 1em; +} #wpadminbar #distributor-push-wrapper .new-connections-list > div:nth-child(odd) { background-color: var(--backgroundColorDark); @@ -270,7 +278,9 @@ box-sizing: border-box; } -#wpadminbar #distributor-push-wrapper .action-wrapper.loading .syndicate-button:after { +#wpadminbar #distributor-push-wrapper .action-wrapper.loading .syndicate-button:after, +#wpadminbar #distributor-push-wrapper .action-wrapper.loading .selectall-connections:after, +#wpadminbar #distributor-push-wrapper .action-wrapper.loading .selectno-connections:after { content: ' '; vertical-align: middle; border-radius: 50%; diff --git a/assets/js/admin-external-connection.js b/assets/js/admin-external-connection.js index f6b3f392b..72c450ef7 100755 --- a/assets/js/admin-external-connection.js +++ b/assets/js/admin-external-connection.js @@ -2,16 +2,16 @@ import jQuery from 'jquery'; import _ from 'underscores'; import { dt, ajaxurl } from 'window'; -const externalConnectionUrlField = document.getElementsByClassName( 'external-connection-url-field' )[0]; -const externalConnectionMetaBox = document.getElementById( 'dt_external_connection_details' ); -const externalConnectionTypeField = document.getElementsByClassName( 'external-connection-type-field' )[0]; -const authFields = document.getElementsByClassName( 'auth-field' ); -const rolesAllowed = document.getElementsByClassName( 'dt-roles-allowed' ); -const titleField = document.getElementById( 'title' ); -const endpointResult = document.querySelector( '.endpoint-result' ); -const endpointErrors = document.querySelector( '.endpoint-errors' ); -const postIdField = document.getElementById( 'post_ID' ); -let $apiVerify = false; +const [ externalConnectionUrlField ] = document.getElementsByClassName( 'external-connection-url-field' ); +const externalConnectionMetaBox = document.getElementById( 'dt_external_connection_details' ); +const [ externalConnectionTypeField ] = document.getElementsByClassName( 'external-connection-type-field' ); +const authFields = document.getElementsByClassName( 'auth-field' ); +const rolesAllowed = document.getElementsByClassName( 'dt-roles-allowed' ); +const titleField = document.getElementById( 'title' ); +const endpointResult = document.querySelector( '.endpoint-result' ); +const endpointErrors = document.querySelector( '.endpoint-errors' ); +const postIdField = document.getElementById( 'post_ID' ); +let $apiVerify = false; /** * Check the external connection. @@ -41,7 +41,7 @@ function checkConnections() { return; } - var key = authField.getAttribute( 'data-auth-field' ); + const key = authField.getAttribute( 'data-auth-field' ); if ( key ) { auth[key] = authField.value; @@ -72,7 +72,7 @@ function checkConnections() { endpointResult.setAttribute( 'data-endpoint-state', 'error' ); if ( response.data.endpoint_suggestion ) { - endpointResult.innerText = dt.endpoint_suggestion + ' '; + endpointResult.innerText = `${ dt.endpoint_suggestion } `; const suggestion = document.createElement( 'a' ); suggestion.classList.add( 'suggest' ); @@ -90,9 +90,9 @@ function checkConnections() { const warnings = []; if ( response.data.errors.no_distributor ) { - endpointResult.innerText += ' ' + dt.no_distributor; + endpointResult.innerText += ` ${ dt.no_distributor }`; } else { - endpointResult.innerText += ' ' + dt.bad_auth; + endpointResult.innerText += ` ${ dt.bad_auth }`; } warnings.push( dt.no_push ); @@ -228,7 +228,7 @@ jQuery( externalConnectionTypeField ).on( 'change', () => { const slug = externalConnectionTypeField.value; $authCredentials.hide(); - jQuery( '.auth-credentials.' + slug ).show(); + jQuery( `.auth-credentials.${ slug }` ).show(); // For WordPress.com Oauth authentication, hide fields until authentication is complete. if ( 'wpdotcom' === slug ) { @@ -316,7 +316,7 @@ if ( beginAuthorize ) { if ( response.success && response.data.id ) { // The post has been saved, update the url in case the user refreshes. - const url = dt.admin_url + 'post.php?post=' + response.data.id + '&action=edit'; + const url = `${ dt.admin_url }post.php?post=${ response.data.id }&action=edit`; history.pushState( {}, 'Oauth Authorize Details', url ); // Update the form field for dt_redirect_uri and post id. diff --git a/assets/js/admin-pull.js b/assets/js/admin-pull.js index 636ec098b..342ec4ede 100755 --- a/assets/js/admin-pull.js +++ b/assets/js/admin-pull.js @@ -33,9 +33,9 @@ if ( chooseConnection && choosePostType && form ) { const search = searchField.value; - document.location = getURL() + '&s=' + search; + document.location = `${ getURL() }&s=${ search }`; - document.body.className += ' ' + 'dt-loading'; + document.body.className += ' dt-loading'; } ); } } @@ -50,11 +50,11 @@ const getURL = () => { const baseURL = chooseConnection.options[ chooseConnection.selectedIndex ].getAttribute( 'data-pull-url' ); let status = 'new'; - if ( -1 < ( ' ' + form.className + ' ' ).indexOf( ' status-skipped ' ) ) { + if ( -1 < ( ` ${ form.className } ` ).indexOf( ' status-skipped ' ) ) { status = 'skipped'; - } else if ( -1 < ( ' ' + form.className + ' ' ).indexOf( ' status-pulled ' ) ) { + } else if ( -1 < ( ` ${ form.className } ` ).indexOf( ' status-pulled ' ) ) { status = 'pulled'; } - return baseURL + '&pull_post_type=' + postType + '&status=' + status; + return `${ baseURL }&pull_post_type=${ postType }&status=${ status }`; }; diff --git a/assets/js/push.js b/assets/js/push.js index b8a2845fe..8b09c7516 100755 --- a/assets/js/push.js +++ b/assets/js/push.js @@ -4,7 +4,6 @@ import { dt } from 'window'; let selectedConnections = {}, searchString = ''; - const processTemplate = _.memoize( ( id ) => { const element = document.getElementById( id ); if ( ! element ) { @@ -29,14 +28,18 @@ jQuery( window ).on( 'load', () => { return; } - let dtConnections = ''; - let connectionsSelected = ''; - let connectionsSelectedList = ''; - let connectionsNewList = ''; - let connectionsSearchInput = ''; - let actionWrapper = ''; - let postStatusInput = ''; - let asDraftInput = ''; + let dtConnections = ''; + let connectionsSelected = ''; + let connectionsSelectedList = ''; + let connectionsNewList = ''; + let connectionsNewListChildren = ''; + let connectionsAvailableTotal = ''; + let selectAllConnections = ''; + let selectNoConnections = ''; + let connectionsSearchInput = ''; + let actionWrapper = ''; + let postStatusInput = ''; + let asDraftInput = ''; distributorMenuItem.appendChild( distributorPushWrapper ); @@ -44,13 +47,19 @@ jQuery( window ).on( 'load', () => { * Set variables after connections have been rendered */ function setVariables() { - connectionsSelected = distributorPushWrapper.querySelector( '.connections-selected' ); - connectionsSelectedList = distributorPushWrapper.querySelector( '.selected-connections-list' ); - connectionsNewList = distributorPushWrapper.querySelector( '.new-connections-list' ); - connectionsSearchInput = document.getElementById( 'dt-connection-search' ); - actionWrapper = distributorPushWrapper.querySelector( '.action-wrapper' ); - postStatusInput = document.getElementById( 'dt-post-status' ); - asDraftInput = document.getElementById( 'dt-as-draft' ); + connectionsSelected = distributorPushWrapper.querySelector( '.connections-selected' ); + connectionsSelectedList = distributorPushWrapper.querySelector( '.selected-connections-list' ); + connectionsNewList = distributorPushWrapper.querySelector( '.new-connections-list' ); + selectAllConnections = distributorPushWrapper.querySelector( '.selectall-connections' ); + selectNoConnections = distributorPushWrapper.querySelector( '.selectno-connections' ); + connectionsSearchInput = document.getElementById( 'dt-connection-search' ); + actionWrapper = distributorPushWrapper.querySelector( '.action-wrapper' ); + postStatusInput = document.getElementById( 'dt-post-status' ); + asDraftInput = document.getElementById( 'dt-as-draft' ); + + if ( null !== connectionsNewList ){ + connectionsNewListChildren = connectionsNewList.querySelectorAll( '.add-connection' ); + } /** * Listen for connection filtering @@ -59,11 +68,20 @@ jQuery( window ).on( 'load', () => { if ( '' === event.currentTarget.value ) { showConnections( dtConnections ); } - searchString = event.currentTarget.value.replace( /https?:\/\//i, '' ).replace( /www/i, '' ).replace( /[^0-9a-zA-Z ]+/, '' ); - showConnections(); }, 300 ) ); + + /** + * Disable select all button if all connections are syndicated and set variable for total connections available + */ + _.each( connectionsNewListChildren, ( element ) => { + if ( !element.classList.contains ( 'syndicated' ) ) { + selectAllConnections.classList.remove( 'unavailable' ); + connectionsAvailableTotal ++; + } + } ); + } /** @@ -87,7 +105,7 @@ jQuery( window ).on( 'load', () => { if ( 'fail' === result.status ) { error = true; } else { - dtConnections['internal' + connectionId].syndicated = result.url; + dtConnections[ `internal${ connectionId}` ].syndicated = result.url; } } ); @@ -95,7 +113,7 @@ jQuery( window ).on( 'load', () => { if ( 'fail' === result.status ) { error = true; } else { - dtConnections['external' + connectionId].syndicated = true; + dtConnections[ `external${ connectionId }` ].syndicated = true; } } ); @@ -118,15 +136,15 @@ jQuery( window ).on( 'load', () => { } /** - * Show connections. If there is a search string, then filter by it - */ + * Show connections. If there is a search string, then filter by it + */ function showConnections() { connectionsNewList.innerText = ''; _.each( dtConnections, ( connection ) => { if ( '' !== searchString ) { - let nameMatch = connection.name.replace( /[^0-9a-zA-Z ]+/, '' ).toLowerCase().match( searchString.toLowerCase() ); - let urlMatch = connection.url.replace( /https?:\/\//i, '' ).replace( /www/i, '' ).replace( /[^0-9a-zA-Z ]+/, '' ).toLowerCase().match( searchString.toLowerCase() ); + const nameMatch = connection.name.replace( /[^0-9a-zA-Z ]+/, '' ).toLowerCase().match( searchString.toLowerCase() ); + const urlMatch = connection.url.replace( /https?:\/\//i, '' ).replace( /www/i, '' ).replace( /[^0-9a-zA-Z ]+/, '' ).toLowerCase().match( searchString.toLowerCase() ); if ( ! nameMatch && ! urlMatch ) { return; @@ -142,6 +160,32 @@ jQuery( window ).on( 'load', () => { } ); } + /** + * Add or remove CSS classes to indicate button functionality + */ + function classList( expr ) { + switch ( expr ) { + case 'addEmpty': + connectionsSelected.classList.add( 'empty' ); + break; + case 'removeEmpty': + connectionsSelected.classList.remove( 'empty' ); + break; + case 'allUnavailable': + selectAllConnections.classList.add ( 'unavailable' ); + break; + case 'all': + selectAllConnections.classList.remove ( 'unavailable' ); + break; + case 'noneUnavailable': + selectNoConnections.classList.add ( 'unavailable' ); + break; + case 'none': + selectNoConnections.classList.remove ( 'unavailable' ); + break; + } + } + /** * Handle distributor push dropdown menu hover using hoverIntent. */ @@ -178,7 +222,6 @@ jQuery( window ).on( 'load', () => { dtConnections = response.data; - // Allowing innerHTML because processTemplate escapes values distributorPushWrapper.innerHTML = processTemplate( 'dt-show-connections' )( { connections: dtConnections, } ); @@ -261,30 +304,30 @@ jQuery( window ).on( 'load', () => { } if ( event.currentTarget.classList.contains( 'added' ) ) { - const type = event.currentTarget.getAttribute( 'data-connection-type' ); const id = event.currentTarget.getAttribute( 'data-connection-id' ); - const deleteNode = connectionsSelectedList.querySelector( '[data-connection-id="' + id + '"][data-connection-type="' + type + '"]' ); + const deleteNode = connectionsSelectedList.querySelector( `[data-connection-id="${ id }"][data-connection-type="${ type }"]` ); deleteNode.parentNode.removeChild( deleteNode ); delete selectedConnections[type + id]; + if ( selectAllConnections.classList.contains ( 'unavailable' ) ) { + classList ( 'all' ); + } if ( ! Object.keys( selectedConnections ).length ) { - connectionsSelected.classList.add( 'empty' ); + classList ( 'addEmpty' ); + classList ( 'noneUnavailable' ); } showConnections(); } else { - const type = event.currentTarget.getAttribute( 'data-connection-type' ); const id = event.currentTarget.getAttribute( 'data-connection-id' ); selectedConnections[type + id] = dtConnections[type + id]; - connectionsSelected.classList.remove( 'empty' ); - const element = event.currentTarget.cloneNode(); element.innerText = event.currentTarget.innerText; @@ -296,10 +339,80 @@ jQuery( window ).on( 'load', () => { connectionsSelectedList.appendChild( element ); + if ( selectNoConnections.classList.contains ( 'unavailable' ) ) { + classList ( 'removeEmpty' ); + classList ( 'none' ); + } + + if ( Object.keys( selectedConnections ).length == connectionsAvailableTotal ){ + classList ( 'allUnavailable' ); + } + showConnections(); } } ); + /** + * Select all connections for distribution. + */ + jQuery( distributorPushWrapper ).on( 'click', '.selectall-connections', () => { + jQuery ( connectionsNewList ).children( '.add-connection' ).each( ( index, childTarget ) => { + if ( childTarget.classList.contains( 'syndicated' ) || childTarget.classList.contains( 'added' ) ) { + return; + } else { + const type = childTarget.getAttribute( 'data-connection-type' ); + const id = childTarget.getAttribute( 'data-connection-id' ); + + selectedConnections[type + id] = dtConnections[type + id]; + + const element = childTarget.cloneNode(); + element.innerText = childTarget.innerText; + + const removeLink = document.createElement( 'span' ); + removeLink.classList.add( 'remove-connection' ); + + element.appendChild( removeLink ); + element.classList = 'added-connection'; + + connectionsSelectedList.appendChild( element ); + + } + + if ( '' !== connectionsAvailableTotal ) { + classList ( 'removeEmpty' ); + classList ( 'allUnavailable' ); + classList ( 'none' ); + } + + } ); + + showConnections(); + } ); + + /** + * Select no connections for distribution. + */ + jQuery( distributorPushWrapper ).on( 'click', '.selectno-connections', () => { + + while ( connectionsSelectedList.firstChild ) { + const type = connectionsSelectedList.firstChild.getAttribute( 'data-connection-type' ); + const id = connectionsSelectedList.firstChild.getAttribute( 'data-connection-id' ); + + delete selectedConnections[type + id]; + + connectionsSelectedList.removeChild( connectionsSelectedList.firstChild ); + + } + + if ( '' !== connectionsAvailableTotal ) { + classList ( 'addEmpty' ); + classList ( 'noneUnavailable' ); + classList ( 'all' ); + } + + showConnections(); + } ); + /** * Remove a connection from selected connections and the UI list */ @@ -310,8 +423,12 @@ jQuery( window ).on( 'load', () => { delete selectedConnections[type + id]; + if ( selectAllConnections.classList.contains ( 'unavailable' ) ) { + classList ( 'all' ); + } if ( ! Object.keys( selectedConnections ).length ) { - connectionsSelected.classList.add( 'empty' ); + classList ( 'addEmpty' ); + classList ( 'noneUnavailable' ); } showConnections(); diff --git a/composer.json b/composer.json index d40fad516..a5fac8e58 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,8 @@ ], "require": { "php": ">=5.6", - "yahnis-elsts/plugin-update-checker": "^4.4" + "yahnis-elsts/plugin-update-checker": "^4.4", + "ext-json": "*" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index 888569aec..be94fe82c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "930a5a93d964fc7fa225c6c6dfbce835", + "content-hash": "dd224472f7d58e7774ce04197e97f717", "packages": [ { "name": "yahnis-elsts/plugin-update-checker", @@ -133,19 +133,19 @@ "source": { "type": "git", "url": "https://github.com/10up/wpacceptance.git", - "reference": "9e9dfb1fdd76d6916b8164a952453137cf7551a1" + "reference": "41f51fd6ee09fcf84a30db82915bc03c928b6520" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/10up/wpacceptance/zipball/9e9dfb1fdd76d6916b8164a952453137cf7551a1", - "reference": "9e9dfb1fdd76d6916b8164a952453137cf7551a1", + "url": "https://api.github.com/repos/10up/wpacceptance/zipball/41f51fd6ee09fcf84a30db82915bc03c928b6520", + "reference": "41f51fd6ee09fcf84a30db82915bc03c928b6520", "shasum": "" }, "require": { "10up/wpinstructions": "dev-master", "10up/wpsnapshots": "dev-master", - "docker-php/docker-php": "^2.0", - "facebook/webdriver": "^1.6", + "docker-php/docker-php": "^2.0@dev", + "jane-php/open-api-runtime": "4.2.0", "nesk/puphpeteer": "^1.4", "php": ">=7.2", "phpunit/phpunit": "^7.5", @@ -182,7 +182,7 @@ "testing", "wordpress" ], - "time": "2019-04-05T20:41:31+00:00" + "time": "2019-07-18T13:18:45+00:00" }, { "name": "10up/wpinstructions", @@ -243,12 +243,12 @@ "source": { "type": "git", "url": "https://github.com/10up/wpsnapshots.git", - "reference": "9dcb8df741e0acb9582295e1869faa04b829e16d" + "reference": "ba0b035be901458d1217f9eb9ca1a2ee390bc4c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/10up/wpsnapshots/zipball/9dcb8df741e0acb9582295e1869faa04b829e16d", - "reference": "9dcb8df741e0acb9582295e1869faa04b829e16d", + "url": "https://api.github.com/repos/10up/wpsnapshots/zipball/ba0b035be901458d1217f9eb9ca1a2ee390bc4c4", + "reference": "ba0b035be901458d1217f9eb9ca1a2ee390bc4c4", "shasum": "" }, "require": { @@ -288,7 +288,7 @@ "snapshots", "wordpress" ], - "time": "2019-04-05T20:35:41+00:00" + "time": "2019-05-29T20:39:21+00:00" }, { "name": "antecedent/patchwork", @@ -4357,7 +4357,7 @@ }, { "name": "Gert de Pagter", - "email": "backendtea@gmail.com" + "email": "BackEndTea@gmail.com" } ], "description": "Symfony polyfill for ctype functions", @@ -4765,12 +4765,12 @@ "version": "2.1.0", "source": { "type": "git", - "url": "https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards.git", + "url": "https://github.com/WordPress/WordPress-Coding-Standards.git", "reference": "8c7a2e7682de9ef5955251874b639deda51ef470" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/WordPress-Coding-Standards/WordPress-Coding-Standards/zipball/8c7a2e7682de9ef5955251874b639deda51ef470", + "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/8c7a2e7682de9ef5955251874b639deda51ef470", "reference": "8c7a2e7682de9ef5955251874b639deda51ef470", "shasum": "" }, @@ -4815,7 +4815,8 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": ">=5.6" + "php": ">=5.6", + "ext-json": "*" }, "platform-dev": [] } diff --git a/distributor.php b/distributor.php index a4f44d998..4891db78c 100644 --- a/distributor.php +++ b/distributor.php @@ -158,7 +158,9 @@ function() { * @since 1.0.0 * @hook dt_network_site_connection_enabled * - * @param bool true Whether the network connection should be enabled. + * @param {bool} true Whether the network connection should be enabled. + * + * @return {bool} Whether the network connection should be enabled. */ apply_filters( 'dt_network_site_connection_enabled', true ) ) { diff --git a/gulp-tasks/cssclean.js b/gulp-tasks/cssclean.js index 3988c1f17..e7599b5d1 100644 --- a/gulp-tasks/cssclean.js +++ b/gulp-tasks/cssclean.js @@ -2,5 +2,5 @@ import gulp from 'gulp'; import del from 'del'; gulp.task( 'cssclean', () => { - del( ['./dist/**/*.css*'] ); + return del( ['./dist/**/*.css*'] ); } ); diff --git a/gulp-tasks/csscomplete.js b/gulp-tasks/csscomplete.js index e5aee8a93..0c3dfc832 100644 --- a/gulp-tasks/csscomplete.js +++ b/gulp-tasks/csscomplete.js @@ -2,5 +2,5 @@ import gulp from 'gulp'; import del from 'del'; gulp.task( 'csscomplete', () => { - del( ['./dist/*.css*'] ); -} ); \ No newline at end of file + return del( ['./dist/*.css*'] ); +} ); diff --git a/gulp-tasks/cssnano.js b/gulp-tasks/cssnano.js index 5853e3fc2..e103e463d 100644 --- a/gulp-tasks/cssnano.js +++ b/gulp-tasks/cssnano.js @@ -1,10 +1,11 @@ import gulp from 'gulp'; -import cssnano from 'gulp-cssnano'; +import postcss from 'gulp-postcss'; import rename from 'gulp-rename'; import sourcemaps from 'gulp-sourcemaps'; import pump from 'pump'; import livereload from 'gulp-livereload'; import filter from 'gulp-filter'; +import cssnano from 'cssnano'; /** * Gulp task to run the cssnano task. @@ -17,21 +18,16 @@ import filter from 'gulp-filter'; */ gulp.task( 'cssnano', ( cb ) => { const fileDest = './dist/css', + plugins = [ + cssnano() + ], fileSrc = [ './dist/*.css' - ], - taskOpts = { - autoprefixer: false, - calc: { - precision: 8 - }, - zindex: false, - convertValues: true - }; + ]; pump( [ gulp.src( fileSrc ), - cssnano( taskOpts ), + postcss(plugins), rename( function( path ) { path.extname = '.min.css'; } ), diff --git a/gulp-tasks/jsclean.js b/gulp-tasks/jsclean.js index d1e8e2599..e01c463b6 100644 --- a/gulp-tasks/jsclean.js +++ b/gulp-tasks/jsclean.js @@ -2,5 +2,5 @@ import gulp from 'gulp'; import del from 'del'; gulp.task( 'jsclean', () => { - del( ['./dist/**/*.js*'] ); -} ); \ No newline at end of file + return del( ['./dist/**/*.js*'] ); +} ); diff --git a/gulp-tasks/webpack.js b/gulp-tasks/webpack.js index abb870d36..7d0f65b81 100644 --- a/gulp-tasks/webpack.js +++ b/gulp-tasks/webpack.js @@ -22,9 +22,10 @@ function processWebpack( src, conf, dest, cb ) { * @param {Function} cb the pipe sequence that gulp should run. * @returns {void} */ -gulp.task( 'webpack', () => { +gulp.task( 'webpack', ( complete ) => { const src = '../assets/js/*.js'; const conf = '../webpack.config.babel.js'; const dest = './dist/js'; processWebpack( src, conf, dest ); + complete(); } ); diff --git a/gulpfile.babel.js b/gulpfile.babel.js index e2c219bef..c166ba201 100644 --- a/gulpfile.babel.js +++ b/gulpfile.babel.js @@ -1,6 +1,6 @@ import gulp from 'gulp'; import requireDir from 'require-dir'; -import runSequence from 'run-sequence'; +import runSequence from 'gulp4-run-sequence'; import livereload from 'gulp-livereload'; requireDir( './gulp-tasks' ); @@ -8,22 +8,24 @@ requireDir( './gulp-tasks' ); /** * Gulp task to run all JS processes in a sequential order. */ -gulp.task( 'js', () => { - runSequence( +gulp.task( 'js', ( callback ) => { + return runSequence( 'jsclean', - 'webpack' + 'webpack', + callback() ); } ); /** * Gulp task to run all Sass/CSS processes in a sequential order. */ -gulp.task( 'css', () => { - runSequence( +gulp.task( 'css', ( callback ) => { + return runSequence( 'cssclean', 'cssnext', 'cssnano', - 'csscomplete' + 'csscomplete', + callback() ); } ); @@ -39,20 +41,22 @@ gulp.task( 'watch', () => { /** * Gulp task to run the default release processes in a sequential order. */ -gulp.task( 'release', () => { - runSequence( +gulp.task( 'release', ( callback ) => { + return runSequence( 'css', 'js', - 'copy' + 'copy', + callback() ); } ); /** * Gulp task to run the default build processes in a sequential order. */ -gulp.task( 'default', () => { - runSequence( +gulp.task( 'default', ( callback ) => { + return runSequence( 'css', - 'js' + 'js', + callback() ); } ); diff --git a/hookdoc-conf.json b/hookdoc-conf.json index 738a7d641..12791a824 100644 --- a/hookdoc-conf.json +++ b/hookdoc-conf.json @@ -2,12 +2,24 @@ "opts": { "destination": "docs", "template": "node_modules/wp-hookdoc/template", - "recurse": true + "recurse": true, + "readme": "./.github/hookdoc-tmpl/README.md" }, "source": { "includePattern": ".+\\.(php|inc)?$" }, "plugins": [ - "node_modules/wp-hookdoc/plugin" - ] + "node_modules/wp-hookdoc/plugin", + "plugins/markdown" + ], + "templates": { + "default": { + "layoutFile": ".github/hookdoc-tmpl/layout.tmpl", + "staticFiles": { + "include": [ + "./.github/hookdoc-tmpl/static" + ] + } + } + } } diff --git a/includes/.github/workflows/build-docs.yml b/includes/.github/workflows/build-docs.yml deleted file mode 100644 index e856ce2be..000000000 --- a/includes/.github/workflows/build-docs.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Node CI - -on: push: - branches: - - develop - -jobs: - build: - - runs-on: ubuntu-latest - node-version: [12.x] - steps: - - uses: actions/checkout@v1 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node-version }} - - name: npm install, and build docs - run: | - npm install - npm run build:docs - env: - CI: true - deploy: - - name: GitHub Pages Deploy - - uses: maxheld83/ghpages@v0.2.1 - - env: - BUILD_DIR = "docs/" - secrets = ["GH_PAT"] diff --git a/includes/classes/API/SubscriptionsController.php b/includes/classes/API/SubscriptionsController.php index 31e670338..1f84f92b3 100644 --- a/includes/classes/API/SubscriptionsController.php +++ b/includes/classes/API/SubscriptionsController.php @@ -292,8 +292,8 @@ public function receive_item( $request ) { * @since 1.3.8 * @hook dt_process_subscription_attributes * - * @param WP_Post $post Updated post object. - * @param WP_REST_Request $request Request object. + * @param {WP_Post} $post Updated post object. + * @param {WP_REST_Request} $request Request object. */ do_action( 'dt_process_subscription_attributes', $post, $request ); diff --git a/includes/classes/Authentication.php b/includes/classes/Authentication.php index adfc14ae2..ba54333a9 100644 --- a/includes/classes/Authentication.php +++ b/includes/classes/Authentication.php @@ -48,14 +48,16 @@ public function __construct( $args ) { */ public function format_get_args( $args = array(), $context = array() ) { /** - * Format request args for a GET request so auth occurs + * Format request args for a GET request so auth occurs. * * @since 0.8 * @hook dt_auth_format_get_args * - * @param array $args - * @param array $context optional array of information about the request - * @param object $this The authentication class. + * @param {array} $args Array of request arguments. + * @param {array} $context Optional array of information about the request. + * @param {object} $this The authentication class. + * + * @return {array} Array of request arguments. */ return apply_filters( 'dt_auth_format_get_args', $args, $context, $this ); } @@ -75,9 +77,11 @@ public function format_post_args( $args, $context = array() ) { * @since 0.8 * @hook dt_auth_format_post_args * - * @param array $args - * @param array $context optional array of information about the request - * @param object $this The authentication class. + * @param {array} $args Array of request arguments. + * @param {array} $context Optional array of information about the request. + * @param {object} $this The authentication class. + * + * @return {array} Array of request arguments. */ return apply_filters( 'dt_auth_format_post_args', $args, $context, $this ); } diff --git a/includes/classes/Authentications/WordPressBasicAuth.php b/includes/classes/Authentications/WordPressBasicAuth.php index 081ef91ec..057024221 100644 --- a/includes/classes/Authentications/WordPressBasicAuth.php +++ b/includes/classes/Authentications/WordPressBasicAuth.php @@ -120,9 +120,11 @@ public static function prepare_credentials( $args ) { * @since 1.0 * @hook dt_auth_prepare_credentials * - * @param array $auth The credentials to be saved. - * @param array $args The arguments originally passed to .prepare_credentials'. - * @param string $slug The authorization handler type slug. + * @param {array} $auth The credentials to be saved. + * @param {array} $args The arguments originally passed to `prepare_credentials`. + * @param {string} $slug The authorization handler type slug. + * + * @return {array} The authorization credentials to be saved. */ return apply_filters( 'dt_auth_prepare_credentials', $auth, $args, self::$slug ); } diff --git a/includes/classes/Authentications/WordPressDotcomOauth2Authentication.php b/includes/classes/Authentications/WordPressDotcomOauth2Authentication.php index dc4cecaea..791165e68 100644 --- a/includes/classes/Authentications/WordPressDotcomOauth2Authentication.php +++ b/includes/classes/Authentications/WordPressDotcomOauth2Authentication.php @@ -88,8 +88,11 @@ public static function credentials_form( $args = array() ) { ); $args[ self::API_REDIRECT_URI ] = $redirect_uri; - // Display any authorization or token errors. - // @hook dt_oauth_admin_notices + /** + * Display any authorization or token errors. + * + * @hook dt_oauth_admin_notices + */ do_action( 'dt_oauth_admin_notices' ); // If anything is missing, we aren't authorized - show the credentials form. diff --git a/includes/classes/ExternalConnection.php b/includes/classes/ExternalConnection.php index 7fc869072..1dc4f3bd7 100644 --- a/includes/classes/ExternalConnection.php +++ b/includes/classes/ExternalConnection.php @@ -98,9 +98,9 @@ public function log_sync( array $item_id_mappings, $connection_id = 0 ) { * @since 1.0 * @hook dt_log_sync * - * @param array $item_id_mappings Item ID mappings. - * @param array $sync_log The sync log - * @param object $this This class. + * @param {array} $item_id_mappings Item ID mappings. + * @param {array} $sync_log The sync log + * @param {object} $this The current connection class. */ do_action( 'dt_log_sync', $item_id_mappings, $sync_log, $this ); } diff --git a/includes/classes/ExternalConnections/WordPressExternalConnection.php b/includes/classes/ExternalConnections/WordPressExternalConnection.php index 3935c985a..7f452d3c7 100644 --- a/includes/classes/ExternalConnections/WordPressExternalConnection.php +++ b/includes/classes/ExternalConnections/WordPressExternalConnection.php @@ -127,8 +127,10 @@ public function remote_get( $args = array() ) { * @since 1.0 * @hook dt_remote_get * - * @param array $args The arguments originally passed to remote_get. - * @param object $this The authentication class. + * @param {array} $args The arguments originally passed to `remote_get`. + * @param {object} $this The authentication class. + * + * @return {array} The arguments originally passed to `remote_get`. */ return apply_filters( 'dt_remote_get', @@ -234,9 +236,11 @@ public function remote_get( $args = array() ) { * @since 1.0 * @hook dt_remote_get_query_args * - * @param array $query_args The existing query arguments. - * @param array $args The arguments originally passed to .remote_get'. - * @param object $this The authentication class. + * @param {array} $query_args The existing query arguments. + * @param {array} $args The arguments originally passed to `remote_get`. + * @param {object} $this The authentication class. + * + * @return {array} The existing query arguments. */ $query_args = apply_filters( 'dt_remote_get_query_args', $query_args, $args, $this ); @@ -292,9 +296,11 @@ public function remote_get( $args = array() ) { * @since 1.0 * @hook dt_remote_get_url * - * @param string $posts_url The posts URL - * @param string $args The arguments originally passed to .remote_get'. - * @param object $this The authentication class. + * @param {string} $posts_url The posts URL + * @param {string} $args The arguments originally passed to `remote_get`. + * @param {object} $this The authentication class. + * + * @return {string} The posts URL. */ apply_filters( 'dt_remote_get_url', $posts_url, $args, $this ), false, @@ -417,10 +423,12 @@ public function pull( $items ) { * @since 1.0 * @hook dt_pull_post_args * - * @param array $post_array The post to be inserted. - * @param array $item_array['remote_post_id'] The remote post ID. - * @param object $post The request that got the post. - * @param ExternalConnection $this The distributor connection pulling the post. + * @param {array} $post_array The post data to be inserted. + * @param {array} $remote_post_id The remote post ID. + * @param {object} $post The request that got the post. + * @param {ExternalConnection} $this The Distributor connection pulling the post. + * + * @return {array} The post data to be inserted. */ $new_post = wp_insert_post( apply_filters( 'dt_pull_post_args', $post_array, $item_array['remote_post_id'], $post, $this ) ); @@ -451,30 +459,13 @@ public function pull( $items ) { if ( ! empty( $post_array['media'] ) ) { - /** - * Allow bypassing of all media processing. - * - * @param bool true If Distributor should set the post media. - * @param int $new_post The newly created post ID. - * @param array $post_array['media'] List of media items attached to the post, formatted by {@see \Distributor\Utils\prepare_media()}. - * @param int $item_array['remote_post_id'] The original post ID. - * @param array $post_array The arguments passed into wp_insert_post. - * @param ExternalConnection $this The distributor connection being pulled from. - */ + // Filter documented in includes/classes/InternalConnections/NetworkSiteConnection.php. if ( apply_filters( 'dt_pull_post_media', true, $new_post, $post_array['media'], $item_array['remote_post_id'], $post_array, $this ) ) { \Distributor\Utils\set_media( $new_post, $post_array['media'] ); } } - /** - * Action triggered when a post is pulled via distributor. - * - * @hook dt_pull_post - * - * @param int $new_post The newly created post ID. - * @param ExternalConnection $this The distributor connection pulling the post. - * @param array $post_array The original post data retrieved via the connection. - */ + // Filter documented in includes/classes/InternalConnections/NetworkSiteConnection.php. do_action( 'dt_pull_post', $new_post, $this, $post_array ); $created_posts[] = $new_post; @@ -612,41 +603,36 @@ public function push( $post_id, $args = array() ) { $this->auth_handler->format_post_args( array( /** - * Filter the timeout used when calling WordPressExternalConnection::push. + * Filter the timeout used when calling `WordPressExternalConnection::push`. * * @since 1.0 + * @hook dt_push_post_timeout + * + * @param {int} $timeout The timeout to use for the remote post. Default `5`. + * @param {object} $post The post object * - * @param int $timeout The timeout to use for the remote post. Default 5. - * @param object $post The post object + * @return {int} The timeout to use for the remote post. */ 'timeout' => apply_filters( 'dt_push_post_timeout', 45, $post ), /** * Filter the arguments sent to the remote server during a push. * * @since 1.0 + * @hook dt_push_post_args * - * @param array $post_body The request body to send. - * @param object $post The WP_Post that is being pushed. - * @param array $args Post args to push. - * @param ExternalConnection $this The distributor connection being pushed to. + * @param {array} $post_body The request body to send. + * @param {object} $post The WP_Post that is being pushed. + * @param {array} $args Post args to push. + * @param {ExternalConnection} $this The distributor connection being pushed to. + * + * @return {array} The request body to send. */ 'body' => apply_filters( 'dt_push_post_args', $post_body, $post, $args, $this ), ) ) ); - /** - * Action triggered when a post is pushed via distributor. - * - * @hook dt_push_post - * - * @param array $response The HTTP response of the push. - * @param array $post_body The body that was POSTed. - * @param string $type_url The URL that was POSTed to. - * @param int $post_id The post ID that was pushed. - * @param array $args The arguments sent with the POST. - * @param ExternalConnection $this The distributor connection being pushed to. - */ + // Action documented in includes/classes/InternalConnections/NetworkSiteConnection.php. do_action( 'dt_push_post', $response, $post_body, $type_url, $post_id, $args, $this ); if ( is_wp_error( $response ) ) { @@ -945,9 +931,12 @@ private function to_wp_post( $post ) { * Filter the post item. * * @since 1.0 + * @hook dt_item_mapping + * + * @param {WP_Post} $obj The WP_Post that is being pushed. + * @param {ExternalConnection} $this The external connection the post concerns. * - * @param object $obj The WP_Post that is being pushed. - * @param ExternalConnection $this The external connection the post concerns. + * @return {WP_Post} The WP_Post that is being pushed. */ return apply_filters( 'dt_item_mapping', new \WP_Post( $obj ), $post, $this ); } diff --git a/includes/classes/InternalConnections/NetworkSiteConnection.php b/includes/classes/InternalConnections/NetworkSiteConnection.php index eee1a4539..71fef825b 100644 --- a/includes/classes/InternalConnections/NetworkSiteConnection.php +++ b/includes/classes/InternalConnections/NetworkSiteConnection.php @@ -74,7 +74,7 @@ public function push( $post_id, $args = array() ) { 'post_content' => Utils\get_processed_content( $post->post_content ), 'post_excerpt' => $post->post_excerpt, 'post_type' => $post->post_type, - 'post_author' => get_current_user_id(), + 'post_author' => isset( $post->post_author ) ? $post->post_author : get_current_user_id(), 'post_status' => Utils\can_distribute_post_status() ? $post->post_status : 'publish', ); @@ -133,12 +133,14 @@ public function push( $post_id, $args = array() ) { * * @hook dt_push_post_media * - * @param bool true If Distributor should set the post media. - * @param int $new_post_id The newly created post ID. - * @param array $media List of media items attached to the post, formatted by {@see \Distributor\Utils\prepare_media()}. - * @param int $post_id The original post ID. - * @param array $args The arguments passed into wp_insert_post. - * @param NetworkSiteConnection $this The distributor connection being pushed to. + * @param {bool} true If Distributor should push the post media. + * @param {int} $new_post_id The newly created post ID. + * @param {array} $media List of media items attached to the post, formatted by {@link \Distributor\Utils\prepare_media()}. + * @param {int} $post_id The original post ID. + * @param {array} $args The arguments passed into wp_insert_post. + * @param {Connection} $this The distributor connection being pushed to. + * + * @return {bool} If Distributor should push the post media. */ if ( apply_filters( 'dt_push_post_media', true, $new_post_id, $media, $post_id, $args, $this ) ) { \Distributor\Utils\set_media( $new_post_id, $media ); @@ -146,14 +148,14 @@ public function push( $post_id, $args = array() ) { } /** - * Action triggered when a post is pushed via distributor. + * Fires after a post is pushed via Distributor before `restore_current_blog()`. * * @hook dt_push_post * - * @param int $new_post_id The newly created post ID. - * @param int $post_id The original post ID. - * @param array $args The arguments passed into wp_insert_post. - * @param ExternalConnection $this The distributor connection being pushed to. + * @param {int} $new_post_id The newly created post ID. + * @param {int} $post_id The original post ID. + * @param {array} $args The arguments passed into wp_insert_post. + * @param {Connection} $this The Distributor connection being pushed to. */ do_action( 'dt_push_post', $new_post_id, $post_id, $args, $this ); @@ -237,12 +239,14 @@ public function pull( $items ) { * * @hook dt_pull_post_media * - * @param bool true If Distributor should set the post media. - * @param int $new_post_id The newly created post ID. - * @param array $post->media List of media items attached to the post, formatted by {@see \Distributor\Utils\prepare_media()}. - * @param int $item_array['remote_post_id'] The original post ID. - * @param array $post_array The arguments passed into wp_insert_post. - * @param NetworkSiteConnection $this The distributor connection being pulled from. + * @param {bool} true If Distributor should set the post media. + * @param {int} $new_post_id The newly created post ID. + * @param {array} $post->media List of media items attached to the post, formatted by {@link \Distributor\Utils\prepare_media()}. + * @param {int} $remote_post_id The original post ID. + * @param {array} $post_array The arguments passed into wp_insert_post. + * @param {NetworkSiteConnection} $this The Distributor connection being pulled from. + * + * @return {bool} If Distributor should set the post media. */ if ( apply_filters( 'dt_pull_post_media', true, $new_post_id, $post->media, $item_array['remote_post_id'], $post_array, $this ) ) { \Distributor\Utils\set_media( $new_post_id, $post->media ); @@ -274,14 +278,14 @@ public function pull( $items ) { restore_current_blog(); /** - * Action triggered when a post is pulled via distributor. + * Fires after a post is pulled via Distributor and after `restore_current_blog()`. * * @since 1.0 * @hook dt_pull_post * - * @param int $new_post_id The new post ID that was pulled. - * @param ExternalConnection $this The distributor connection pulling the post. - * @param array $post_array The original post data retrieved via the connection. + * @param {int} $new_post_id The new post ID that was pulled. + * @param {Connection} $this The Distributor connection pulling the post. + * @param {array} $post_array The original post data retrieved via the connection. */ do_action( 'dt_pull_post', $new_post_id, $this, $post_array ); @@ -328,16 +332,7 @@ public function log_sync( array $item_id_mappings, $blog_id = 0 ) { update_option( 'dt_sync_log', $sync_log ); - /** - * Action fired when a sync is being logged. - * - * @since 1.0 - * @hook dt_log_sync - * - * @param array $item_id_mappings Item ID mappings. - * @param array $sync_log The sync log - * @param object $this This class. - */ + // Action documented in includes/classes/ExternalConnection.php. do_action( 'dt_log_sync', $item_id_mappings, $sync_log, $this ); } @@ -577,9 +572,12 @@ public static function connect_syndicated_on_untrash( $post_id ) { /** * Update syndicated post when original changes * - * @param int $post_id Post ID. + * @param int|WP_Post $post Post ID or WP_Post + * depending on which action the method is hooked to. */ - public static function update_syndicated( $post_id ) { + public static function update_syndicated( $post ) { + $post = get_post( $post ); + $post_id = $post->ID; if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) || wp_is_post_revision( $post_id ) || ! current_user_can( 'edit_post', $post_id ) ) { return; } @@ -588,6 +586,12 @@ public static function update_syndicated( $post_id ) { return; } + // If using Gutenberg, short circuit early and run this method later to make sure terms and meta are saved before syndicating. + if ( \Distributor\Utils\is_using_gutenberg( $post ) && doing_action( 'save_post' ) && ! isset( $_GET['meta-box-loader'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification + add_action( "rest_after_insert_{$post->post_type}", array( '\Distributor\InternalConnections\NetworkSiteConnection', 'update_syndicated' ) ); + return; + } + $connection_map = get_post_meta( $post_id, 'dt_connection_map', true ); if ( empty( $connection_map ) || ! is_array( $connection_map ) || empty( $connection_map['internal'] ) ) { @@ -667,9 +671,11 @@ public static function get_available_authorized_sites( $context = null ) { * * @see \Distributor\InternalConnections\NetworkSiteConnection::get_available_authorized_sites() * - * @param array $authorized_sites Array of WP_Site object and post type objects the user can edit. + * @param {array} $authorized_sites Array of `WP_Site` object and post type objects the user can edit. * } - * @param string $context The context of the authorization. + * @param {string} $context The context of the authorization. + * + * @return {array} Array of `WP_Site` object and post type objects. */ $authorized_sites = apply_filters( 'dt_pre_get_authorized_sites', array(), $context ); if ( ! empty( $authorized_sites ) ) { @@ -679,15 +685,17 @@ public static function get_available_authorized_sites( $context = null ) { $authorized_sites = self::build_available_authorized_sites( get_current_user_id(), $context ); /** - * Allow plugins to modify the array of authorized sites. + * Filter the array of authorized sites. * * @since 1.2 * @since 1.3.7 Added the `$context` parameter. * @hook dt_authorized_sites * - * @param array $authorized_sites An array of WP_Site objects and the post type objects the user can edit. + * @param {array} $authorized_sites An array of `WP_Site` objects and the post type objects the user can edit. * } - * @param string $context The context of the authorization. + * @param {string} $context The context of the authorization. + * + * @return {array} An array of `WP_Site` objects and the post type objects. */ return apply_filters( 'dt_authorized_sites', $authorized_sites, $context ); } diff --git a/includes/classes/PullListTable.php b/includes/classes/PullListTable.php index 82f6f36cf..23bbc269d 100644 --- a/includes/classes/PullListTable.php +++ b/includes/classes/PullListTable.php @@ -127,19 +127,7 @@ protected function bulk_actions( $which = '' ) { if ( is_null( $this->_actions ) ) { $no_new_actions = $this->get_bulk_actions(); $this->_actions = $this->get_bulk_actions(); - /** - * Filters the list table Bulk Actions drop-down. - * - * The dynamic portion of the hook name, `$this->screen->id`, refers - * to the ID of the current screen, usually a string. - * - * This filter can currently only be used to remove bulk actions. - * - * @since 3.5.0 - * @hook bulk_actions-{$this->screen->id} - * - * @param array $actions An array of the available bulk actions. - */ + // Filter documented in WordPress core. $this->_actions = apply_filters( "bulk_actions-{$this->screen->id}", $this->_actions ); // @codingStandardsIgnoreLine valid filter name $this->_actions = array_intersect_assoc( $this->_actions, $no_new_actions ); $two = ''; @@ -322,6 +310,7 @@ public function column_name( $item ) { $disable = true; } else { $actions = [ + 'pull' => sprintf( '%s', esc_url( wp_nonce_url( admin_url( 'admin.php?page=pull&action=syndicate&_wp_http_referer=' . rawurlencode( $_SERVER['REQUEST_URI'] ) . '&post=' . $item->ID . '&connection_type=' . $connection_type . '&connection_id=' . $connection_id . '&pull_post_type=' . $item->post_type ), 'bulk-distributor_page_pull' ) ), esc_html__( 'Pull', 'distributor' ) ), 'view' => '' . esc_html__( 'View', 'distributor' ) . '', 'skip' => sprintf( '%s', esc_url( wp_nonce_url( admin_url( 'admin.php?page=pull&action=skip&_wp_http_referer=' . rawurlencode( $_SERVER['REQUEST_URI'] ) . '&post=' . $item->ID . '&connection_type=' . $connection_type . '&connection_id=' . $connection_id ), 'dt_skip' ) ), esc_html__( 'Skip', 'distributor' ) ), ]; diff --git a/includes/external-connection-cpt.php b/includes/external-connection-cpt.php index 3d2c86033..67896dc84 100644 --- a/includes/external-connection-cpt.php +++ b/includes/external-connection-cpt.php @@ -575,7 +575,9 @@ function add_menu_item() { * @since 1.0.0 * @hook dt_capabilities * - * @param string manage_options The capability allowed to view external connections. + * @param {string} 'manage_options' The capability allowed to view external connections. + * + * @return {string} The capability allowed to view external connections. */ apply_filters( 'dt_capabilities', 'manage_options' ), 'distributor', @@ -604,7 +606,9 @@ function add_submenu_item() { * @since 1.0.0 * @hook dt_external_capabilities * - * @param string manage_options The capability allowed to manage external connections. + * @param {string} 'manage_options' The capability allowed to manage external connections. + * + * @return {string} The capability allowed to manage external connections. */ apply_filters( 'dt_external_capabilities', 'manage_options' ), 'distributor' diff --git a/includes/pull-ui.php b/includes/pull-ui.php index a93218a1c..f5b107943 100644 --- a/includes/pull-ui.php +++ b/includes/pull-ui.php @@ -134,7 +134,9 @@ function action_admin_menu() { * @since 1.0.0 * @hook dt_pull_capabilities * - * @param string manage_options The capability allowed to pull content. + * @param {string} 'manage_options' The capability allowed to pull content. + * + * @return {string} The capability allowed to pull content. */ apply_filters( 'dt_pull_capabilities', 'manage_options' ), 'pull', diff --git a/includes/push-ui.php b/includes/push-ui.php index 4db63427f..12e8f60d4 100644 --- a/includes/push-ui.php +++ b/includes/push-ui.php @@ -34,7 +34,14 @@ function() { * @return bool */ function syndicatable() { - if ( ! is_user_logged_in() || ! current_user_can( 'edit_posts' ) ) { + /** + * Filter Distributor capabilities allowed to syndicate content. + * + * @hook dt_syndicatable_capabilities + * + * @param string edit_posts The capability allowed to syndicate content. + */ + if ( ! is_user_logged_in() || ! current_user_can( apply_filters( 'dt_syndicatable_capabilities', 'edit_posts' ) ) ) { return false; } @@ -160,7 +167,16 @@ function get_connections() { } // If not admin lets make sure the current user can push to this connection - // @todo Document filter. + /** + * Filter Distributor capabilities allowed to push content. + * + * @since 1.0.0 + * @hook dt_push_capabilities + * + * @param {string} 'manage_options' The capability allowed to push content. + * + * @return {string} The capability allowed to push content. + */ if ( ! current_user_can( apply_filters( 'dt_push_capabilities', 'manage_options' ) ) ) { $current_user_roles = (array) wp_get_current_user()->roles; @@ -362,12 +378,14 @@ function enqueue_scripts( $hook ) { * * Front end ajax requests may require xhrFields with credentials when the front end and * back end domains do not match. This filter lets themes opt in. - * See https://vip.wordpress.com/documentation/handling-frontend-file-uploads/#handling-ajax-requests + * See {@link https://vip.wordpress.com/documentation/handling-frontend-file-uploads/#handling-ajax-requests} * * @since 1.0.0 * @hook dt_ajax_requires_with_credentials * - * @param bool false Whether front end ajax requests should use xhrFields credentials:true. + * @param {bool} false Whether front end ajax requests should use xhrFields credentials:true. + * + * @return {bool} Whether front end ajax requests should use xhrFields credentials:true. */ 'usexhr' => apply_filters( 'dt_ajax_requires_with_credentials', false ), ) @@ -444,6 +462,8 @@ function menu_content() {
+ + <# if ( 5 < _.keys( connections ).length ) { #> <# } #> @@ -485,14 +505,17 @@ function menu_content() { * * @hook dt_allow_as_draft_distribute * - * @param bool $as_draft Whether the 'As Draft' option should appear. - * @param object $connection The connection being used to push. - * @param WP_Post $post The post being pushed. + * @param {bool} $as_draft Whether the 'As Draft' option should appear. + * @param {object} $connection The connection being used to push. + * @param {WP_Post} $post The post being pushed. + * + * @return {bool} Whether the 'As Draft' option should appear. */ $as_draft = apply_filters( 'dt_allow_as_draft_distribute', $as_draft, $connection = null, $post ); ?>
+
diff --git a/includes/rest-api.php b/includes/rest-api.php index c396d43aa..36193db6b 100644 --- a/includes/rest-api.php +++ b/includes/rest-api.php @@ -116,14 +116,14 @@ function process_distributor_attributes( $post, $request, $update ) { } /** - * Action fired after an API push is handled by Distributor. + * Fires after an API push is handled by Distributor. * * @since 1.0 * @hook dt_process_distributor_attributes * - * @param \WP_Post $post Inserted or updated post object. - * @param \WP_REST_Request $request Request object. - * @param bool $update True when creating a post, false when updating. + * @param {WP_Post} $post Inserted or updated post object. + * @param {WP_REST_Request} $request Request object. + * @param {bool} $update True when creating a post, false when updating. */ do_action( 'dt_process_distributor_attributes', $post, $request, $update ); } @@ -228,7 +228,7 @@ function register_endpoints() { 'distributor_original_site_name', array( 'get_callback' => function( $post_array ) { - return get_bloginfo( 'name' ); + return esc_html( get_post_meta( $post_array['id'], 'dt_original_site_name', true ) ); }, 'update_callback' => function( $value, $post ) { }, 'schema' => array( @@ -243,7 +243,7 @@ function register_endpoints() { 'distributor_original_site_url', array( 'get_callback' => function( $post_array ) { - return home_url(); + return esc_url_raw( get_post_meta( $post_array['id'], 'dt_original_site_url', true ) ); }, 'update_callback' => function( $value, $post ) { }, 'schema' => array( diff --git a/includes/subscriptions.php b/includes/subscriptions.php index ce72d5394..bb97fb755 100644 --- a/includes/subscriptions.php +++ b/includes/subscriptions.php @@ -291,8 +291,10 @@ function send_notifications( $post_id ) { * @since 1.3.0 * @hook dt_subscription_post_args * - * @param array $post_body The request body to send. - * @param object $post The WP_Post that is being pushed. + * @param {array} $post_body The request body to send. + * @param {WP_Post} $post The WP_Post that is being pushed. + * + * @return {array} The request body to send. */ 'body' => apply_filters( 'dt_subscription_post_args', $post_body, $post ), ] diff --git a/includes/syndicated-post-ui.php b/includes/syndicated-post-ui.php index cb9ea7ec8..6e90ffc17 100644 --- a/includes/syndicated-post-ui.php +++ b/includes/syndicated-post-ui.php @@ -294,8 +294,10 @@ function unlink() { * @since 1.0 * @hook dt_allow_post_unlink * - * @param bool true Whether the post is allowed to be unlinked. Default true. - * @param int $post_id The ID of the post attempting to be unlinked. + * @param {bool} true Whether the post is allowed to be unlinked. Default true. + * @param {int} $post_id The ID of the post attempting to be unlinked. + * + * @return {bool} Whether the post is allowed to be unlinked. */ ! apply_filters( 'dt_allow_post_unlink', true, $post_id ) ) { return; @@ -307,12 +309,12 @@ function unlink() { * Todo: Do we delete subscriptions for external posts? */ /** - * Action fired when a post is unlinked. + * Fires when a post is unlinked. * * @since 1.0 * @hook dt_unlink_post * - * @param int $post_id ID of the post being unlinked. + * @param {int} $post_id ID of the post being unlinked. */ do_action( 'dt_unlink_post', $post_id ); @@ -384,11 +386,12 @@ function link() { } /** - * Action fired when a post is linked. + * Fires when a post is linked. * * @since 1.0 + * @hook dt_link_post * - * @param int $post_id ID of the post being unlinked. + * @param {int} $post_id ID of the post being linked. */ do_action( 'dt_link_post', $post_id ); diff --git a/includes/template-tags.php b/includes/template-tags.php index 59cea6072..d872bf36b 100644 --- a/includes/template-tags.php +++ b/includes/template-tags.php @@ -173,7 +173,9 @@ function distributor_get_original_site_link( $post_id = null ) { * @since 1.0.0 * @hook distributor_get_original_site_link * - * @param string A formatted version of the original site link. + * @param {string} $link A formatted version of the original site link. + * + * @return {string} A formatted version of the original site link. */ /* translators: %1$s: site url, %2$s; site name*/ return apply_filters( 'distributor_get_original_site_link', sprintf( __( 'By %2$s', 'distributor' ), esc_url( $site_url ), esc_html( $site_name ) ) ); diff --git a/includes/utils.php b/includes/utils.php index 79f919039..d4b4108ef 100644 --- a/includes/utils.php +++ b/includes/utils.php @@ -46,18 +46,14 @@ function is_using_gutenberg( $post ) { return false; } - // WordPress 5.0 introduces the has_blocks function. - // We pass `post_content` directly because `has_blocks()` - // tries to retrieve a local post object, but this may be a remote post. - if ( function_exists( 'has_blocks' ) ) { - return has_blocks( $post->post_content ); + if ( function_exists( 'use_block_editor_for_post' ) ) { + return use_block_editor_for_post( $post ); } else { // This duplicates the check from `has_blocks()` as of WP 5.2. return false !== strpos( (string) $post->post_content, '