diff --git a/assets/css/admin-pull-table.scss b/assets/css/admin-pull-table.scss index a5d37a417..6c22b64cb 100644 --- a/assets/css/admin-pull-table.scss +++ b/assets/css/admin-pull-table.scss @@ -18,6 +18,28 @@ padding-left: 10px; } } + + #distributor-pull-modal{ + position: fixed; + left: 0; + right: 0; + top: 0; + bottom: 0; + background: rgba(0, 0, 0, 50%); + display: flex; + align-items: center; + z-index: 9999; + + &>div{ + margin: 0 auto; + display: block; + width: 300px; + background: white; + padding: 20px; + border-radius: 10px; + box-shadow: 1px 2px 15px rgba(0, 0, 0, 30%); + } + } } .wp-list-table .disabled { diff --git a/assets/js/admin-pull.js b/assets/js/admin-pull.js index 4488187a4..b3474306a 100755 --- a/assets/js/admin-pull.js +++ b/assets/js/admin-pull.js @@ -1,7 +1,6 @@ import '../css/admin-pull-table.scss'; import jQuery from 'jquery'; -import { addQueryArgs } from '@wordpress/url'; import { __ } from '@wordpress/i18n'; const { document, DISTRIBUTOR } = window; @@ -28,6 +27,7 @@ const form = document.getElementById( 'posts-filter' ); const asDraftCheckboxes = document.querySelectorAll( '[name=dt_as_draft]' ); const pullLinks = document.querySelectorAll( '.distributor_page_pull .pull a' ); +// Change target website to pull contents from jQuery( chooseConnection ).on( 'change', ( event ) => { const pullUrlId = event.currentTarget.options[ @@ -39,6 +39,7 @@ jQuery( chooseConnection ).on( 'change', ( event ) => { } ); if ( chooseConnection && choosePostType && form ) { + // Handle post type selection if ( choosePostTypeBtn ) { jQuery( choosePostTypeBtn ).on( 'click', ( event ) => { event.preventDefault(); @@ -49,6 +50,7 @@ if ( chooseConnection && choosePostType && form ) { } ); } + // Handle search button click if ( searchField && searchBtn ) { jQuery( searchBtn ).on( 'click', ( event ) => { event.preventDefault(); @@ -61,33 +63,174 @@ if ( chooseConnection && choosePostType && form ) { } ); } - if ( asDraftCheckboxes && pullLinks ) { + // Handle pull mode checkbox event + if ( asDraftCheckboxes ) { jQuery( asDraftCheckboxes ).on( 'change', ( event ) => { if ( event.currentTarget.checked ) { + // Check all pull mode checkbox as there are multiple. Ideally before and after post list. for ( let i = 0; i < asDraftCheckboxes.length; ++i ) { asDraftCheckboxes[ i ].checked = true; } for ( let i = 0; i < pullLinks.length; ++i ) { - pullLinks[ i ].href = addQueryArgs( pullLinks[ i ].href, { - dt_as_draft: 'draft' /*eslint camelcase: 0*/, - } ); pullLinks[ i ].text = __( 'Pull as draft', 'distributor' ); } } else { + // Uncheck all pull mode checkbox as there are multiple. Ideally before and after post list. for ( let i = 0; i < asDraftCheckboxes.length; ++i ) { asDraftCheckboxes[ i ].checked = false; } for ( let i = 0; i < pullLinks.length; ++i ) { - pullLinks[ i ].href = addQueryArgs( pullLinks[ i ].href, { - dt_as_draft: '' /*eslint camelcase: 0*/, - } ); pullLinks[ i ].text = __( 'Pull', 'distributor' ); } } } ); } + + // Pull content via ajax + jQuery( '#doaction, #doaction2' ).on( 'click', ( e ) => { + // Check action + if ( 'bulk-syndicate' !== jQuery( '[name="action"]' ).val() ) { + return; + } + e.preventDefault(); + openModal(); + } ); + + jQuery( '.distributor_page_pull .pull a' ).on( 'click', function ( e ) { + e.preventDefault(); + jQuery( this ) + .closest( 'tr' ) + .find( '.check-column input[type="checkbox"]' ) + .prop( 'checked', true ); + openModal(); + } ); + + function openModal() { + // Prepare data + let aborted = false; + const postIds = []; + + jQuery( '#the-list .check-column input[type="checkbox"]:checked' ).each( + function () { + const id = parseInt( jQuery( this ).val() ); + if ( id && postIds.indexOf( id ) === -1 ) { + postIds.push( id ); + } + } + ); + + const postIdsCount = postIds.length; + if ( ! postIdsCount ) { + return; + } + + function log( customContent ) { + jQuery( '#distributor-pull-modal .pull-progress' ).html( + customContent || + `Pulled: ${ + postIdsCount - postIds.length + }/${ postIdsCount }` + ); + } + + // Create modal for pulling via ajax + const sourceLabel = jQuery( + '#pull_connections option:selected' + ).text(); + + jQuery( '#distributor-pull-modal' ).remove(); + jQuery( 'body' ).append( + ` +
+
+
+

Pulling from ${ sourceLabel }

+
Selected: ${ postIdsCount }
+
+
+
+ + +
+
+
+ ` + ); + + jQuery( '#distributor-pull-modal' ) + .on( 'click', '[data-action="start"]', function () { + jQuery( this ).prop( 'disabled', true ); + + const excludes = [ 'post[]', 'action2', 'page', 'paged' ]; + const formData = {}; + jQuery( '#posts-filter' ) + .serializeArray() + .forEach( ( field ) => { + if ( excludes.indexOf( field.name ) === -1 ) { + formData[ field.name ] = field.value; + } + } ); + formData.action = 'distributor_pull_content'; + + function looper() { + if ( aborted ) { + jQuery( '#distributor-pull-modal' ).remove(); + } + + log(); + + formData.post_id = postIds.shift(); + const xhr = new window.XMLHttpRequest(); + + jQuery.ajax( { + url: window.ajaxurl, + type: 'POST', + data: formData, + xhr() { + return xhr; + }, + success( resp ) { + if ( aborted ) { + return; + } + + if ( ! resp.success || ! resp.data?.redirect_to ) { + log( + `${ + resp.data?.message || + 'Something went wrong!' + }` + ); + return; + } + + log(); + + if ( postIds.length ) { + // Call the pull again for remaing post + looper(); + } else { + // Redirect to where it asks to + window.location.assign( resp.data.redirect_to ); + } + }, + } ); + } + + looper(); + } ) + .on( 'click', '[data-action="cancel"]', function () { + aborted = true; + jQuery( '#distributor-pull-modal' ).remove(); + + // Refresh the page if any post already pulled. + if ( postIdsCount > postIds.length ) { + window.location.reload(); + } + } ); + } } /** diff --git a/includes/classes/PullListTable.php b/includes/classes/PullListTable.php index 8c1041a88..b597c6d3e 100644 --- a/includes/classes/PullListTable.php +++ b/includes/classes/PullListTable.php @@ -339,7 +339,7 @@ public function column_name( $item ) { $actions = [ //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated -- see `wp_fix_server_vars()`. - 'pull' => sprintf( '%s', esc_url( wp_nonce_url( admin_url( 'admin.php?page=pull&action=syndicate&_wp_http_referer=' . rawurlencode( esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) ) . '&post=' . $item->ID . '&connection_type=' . $connection_type . '&connection_id=' . $connection_id . '&pull_post_type=' . $item->post_type . '&dt_as_draft=' . $draft ), 'bulk-distributor_page_pull' ) ), $draft ? esc_html__( 'Pull as draft', 'distributor' ) : esc_html__( 'Pull', 'distributor' ) ), + 'pull' => sprintf( '%s', $draft ? esc_html__( 'Pull as draft', 'distributor' ) : esc_html__( 'Pull', 'distributor' ) ), 'view' => '' . esc_html__( 'View', 'distributor' ) . '', //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated -- see `wp_fix_server_vars()`. 'skip' => sprintf( '%s', esc_url( wp_nonce_url( admin_url( 'admin.php?page=pull&action=skip&_wp_http_referer=' . rawurlencode( esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) ) . '&post=' . $item->ID . '&connection_type=' . $connection_type . '&connection_id=' . $connection_id ), 'dt_skip' ) ), esc_html__( 'Skip', 'distributor' ) ), @@ -355,7 +355,7 @@ public function column_name( $item ) { $actions = [ //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated -- see `wp_fix_server_vars()`. - 'pull' => sprintf( '%s', esc_url( wp_nonce_url( admin_url( 'admin.php?page=pull&action=syndicate&_wp_http_referer=' . rawurlencode( esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) ) . '&post=' . $item->ID . '&connection_type=' . $connection_type . '&connection_id=' . $connection_id . '&pull_post_type=' . $item->post_type . '&dt_as_draft=' . $draft ), 'bulk-distributor_page_pull' ) ), $draft ? esc_html__( 'Pull as draft', 'distributor' ) : esc_html__( 'Pull', 'distributor' ) ), + 'pull' => sprintf( '%s', $draft ? esc_html__( 'Pull as draft', 'distributor' ) : esc_html__( 'Pull', 'distributor' ) ), //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated -- see `wp_fix_server_vars()`. 'unskip' => sprintf( '%s', esc_url( wp_nonce_url( admin_url( 'admin.php?page=pull&action=unskip&_wp_http_referer=' . rawurlencode( esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) ) . '&post=' . $item->ID . '&connection_type=' . $connection_type . '&connection_id=' . $connection_id ), 'dt_unskip' ) ), esc_html__( 'Unskip', 'distributor' ) ), 'view' => '' . esc_html__( 'View', 'distributor' ) . '', diff --git a/includes/pull-ui.php b/includes/pull-ui.php index b506d0add..cb87d6a85 100644 --- a/includes/pull-ui.php +++ b/includes/pull-ui.php @@ -25,6 +25,8 @@ function() { add_filter( 'set-screen-option', __NAMESPACE__ . '\set_screen_option', 10, 3 ); } ); + + add_action( 'wp_ajax_distributor_pull_content', __NAMESPACE__ . '\pull_contents' ); } /** @@ -206,102 +208,96 @@ function screen_option() { } /** - * Process content changing actions + * Pull contents via ajax call * - * @since 0.8 + * @return void */ -function process_actions() { - global $connection_list_table; +function pull_contents() { global $dt_pull_messages; - switch ( $connection_list_table->current_action() ) { - case 'syndicate': - case 'bulk-syndicate': - if ( empty( $_GET['_wpnonce'] ) || ! wp_verify_nonce( $_GET['_wpnonce'], 'bulk-distributor_page_pull' ) ) { - exit; - } - - // Filter documented above. - if ( ! current_user_can( apply_filters( 'dt_pull_capabilities', 'manage_options' ) ) ) { - wp_die( - '

' . esc_html__( 'Cheatin’ uh?', 'distributor' ) . '

' . - '

' . esc_html__( 'Sorry, you are not allowed to add this item.', 'distributor' ) . '

', - 403 - ); - } - - if ( empty( $_GET['pull_post_type'] ) || empty( $_GET['connection_type'] ) || empty( $_GET['connection_id'] ) || empty( $_GET['post'] ) ) { - break; - } + // Verify nonce + if ( empty( $_POST['_wpnonce'] ) || ! wp_verify_nonce( $_POST['_wpnonce'], 'bulk-distributor_page_pull' ) ) { + wp_send_json_error( array( 'message' => __( 'Invalid Session!', 'distributor' ) ) ); + exit; + } - $posts = array_map( 'intval', (array) wp_unslash( $_GET['post'] ) ); - $post_type = sanitize_text_field( $_GET['pull_post_type'] ); - $post_status = ! empty( $_GET['dt_as_draft'] ) && 'draft' === $_GET['dt_as_draft'] ? 'draft' : ''; - - $posts = array_map( - function( $remote_post_id ) use ( $post_type, $post_status ) { - return [ - 'remote_post_id' => $remote_post_id, - 'post_type' => $post_type, - 'post_status' => $post_status, - ]; - }, - $posts - ); + // Capability check + if ( ! current_user_can( apply_filters( 'dt_pull_capabilities', 'manage_options' ) ) ) { + wp_send_json_error( array( 'message' => __( 'Sorry, you are not allowed to add this item.', 'distributor' ) ) ); + exit; + } - if ( 'external' === $_GET['connection_type'] ) { - $connection = \Distributor\ExternalConnection::instantiate( intval( $_GET['connection_id'] ) ); - $new_posts = $connection->pull( $posts ); - $error_key = "external_{$connection->id}"; + // Input data check + if ( empty( $_POST['pull_post_type'] ) || empty( $_POST['connection_type'] ) || empty( $_POST['connection_id'] ) || empty( $_POST['post_id'] ) ) { + wp_send_json_error( array( 'message' => __( 'Invalid data!', 'distributor' ) ) ); + exit; + } - foreach ( $posts as $key => $post_array ) { - if ( is_wp_error( $new_posts[ $key ] ) ) { - continue; - } - \Distributor\Subscriptions\create_remote_subscription( $connection, $post_array['remote_post_id'], $new_posts[ $key ] ); - } - } else { - $site = get_site( intval( $_GET['connection_id'] ) ); - $connection = new \Distributor\InternalConnections\NetworkSiteConnection( $site ); - $new_posts = $connection->pull( $posts ); - $error_key = "internal_{$connection->site->blog_id}"; - } + // Prepare arguments + $new_post = null; + $post_type = sanitize_text_field( $_POST['pull_post_type'] ); + $post_status = ! empty( $_POST['dt_as_draft'] ) && 'draft' === $_POST['dt_as_draft'] ? 'draft' : ''; + $post = array( + 'remote_post_id' => (int) $_POST['post_id'], + 'post_type' => $post_type, + 'post_status' => $post_status, + ); - $post_id_mappings = array(); - $pull_errors = array(); + // Pull contents external or internal + if ( 'external' === $_POST['connection_type'] ) { + $connection = \Distributor\ExternalConnection::instantiate( intval( $_POST['connection_id'] ) ); + $new_posts = $connection->pull( array( $post ) ); + $new_post = is_array( $new_posts ) ? $new_posts[0] ?? null : null; - foreach ( $posts as $key => $post_array ) { - if ( is_wp_error( $new_posts[ $key ] ) ) { - $pull_errors[ $post_array['remote_post_id'] ] = [ $new_posts[ $key ]->get_error_message() ]; - continue; - } - $post_id_mappings[ $post_array['remote_post_id'] ] = $new_posts[ $key ]; + if ( ! empty( $new_post ) ) { + \Distributor\Subscriptions\create_remote_subscription( $connection, $post['remote_post_id'], $new_post ); + } + } else { + $site = get_site( intval( $_POST['connection_id'] ) ); + $connection = new \Distributor\InternalConnections\NetworkSiteConnection( $site ); + $new_posts = $connection->pull( array( $post ) ); + $new_post = is_array( $new_posts ) ? $new_posts[0] ?? null : null; + } - $media_errors = get_transient( 'dt_media_errors_' . $new_posts[ $key ] ); + if ( empty( $new_post ) || $is_error = is_wp_error( $new_post ) ) { + wp_send_json_error( array( 'message' => $is_error ? $new_post->get_error_message() : __( 'Pull Failed!', 'distributor' ) ) ); + exit; + } - if ( ! empty( $media_errors ) ) { - delete_transient( 'dt_media_errors_' . $new_posts[ $key ] ); - $pull_errors[ $post_array['remote_post_id'] ] = $media_errors; - } - } + // Delete media error + $media_errors = get_transient( 'dt_media_errors_' . $new_post ); + if ( ! empty( $media_errors ) ) { + delete_transient( 'dt_media_errors_' . $new_post ); + } - if ( ! empty( $pull_errors ) ) { - set_transient( 'dt_connection_pull_errors_' . $error_key, $pull_errors, DAY_IN_SECONDS ); - } + // Log mapping + $connection->log_sync( array( $post['remote_post_id'] => $new_post ), 0, false ); - $connection->log_sync( $post_id_mappings ); + if ( empty( $dt_pull_messages['duplicated'] ) ) { + setcookie( 'dt-syndicated', 1, time() + DAY_IN_SECONDS, ADMIN_COOKIE_PATH, COOKIE_DOMAIN, is_ssl() ); + } - if ( empty( $dt_pull_messages['duplicated'] ) ) { - setcookie( 'dt-syndicated', 1, time() + DAY_IN_SECONDS, ADMIN_COOKIE_PATH, COOKIE_DOMAIN, is_ssl() ); - } + if ( ! empty( $dt_pull_messages['duplicated'] ) ) { + setcookie( 'dt-duplicated', 1, time() + DAY_IN_SECONDS, ADMIN_COOKIE_PATH, COOKIE_DOMAIN, is_ssl() ); + } - if ( ! empty( $dt_pull_messages['duplicated'] ) ) { - setcookie( 'dt-duplicated', 1, time() + DAY_IN_SECONDS, ADMIN_COOKIE_PATH, COOKIE_DOMAIN, is_ssl() ); - } + // Redirect to the pulled content tab + wp_send_json_success( + array( + 'redirect_to' => add_query_arg( 'status', 'pulled', wp_get_referer() ), + ) + ); + exit; +} - // Redirect to the pulled content tab - wp_safe_redirect( add_query_arg( 'status', 'pulled', wp_get_referer() ) ); - exit; +/** + * Process content changing actions + * + * @since 0.8 + */ +function process_actions( ) { + global $connection_list_table; + switch ( $connection_list_table->current_action() ) { case 'bulk-skip': case 'skip': if ( ! wp_verify_nonce( $_GET['_wpnonce'], 'dt_skip' ) && ! wp_verify_nonce( $_GET['_wpnonce'], 'bulk-distributor_page_pull' ) ) { @@ -557,8 +553,6 @@ function dashboard() { - - prepare_items(); ?> pull_error ) ) : ?> @@ -593,65 +587,3 @@ function dashboard() { id}"; - } elseif ( is_a( $connection_now, '\Distributor\InternalConnections\NetworkSiteConnection' ) ) { - $error_key = "internal_{$connection_now->site->blog_id}"; - } else { - return; - } - - $pull_errors = get_transient( 'dt_connection_pull_errors_' . $error_key ); - - if ( empty( $pull_errors ) ) { - return; - } - - delete_transient( 'dt_connection_pull_errors_' . $error_key ); - - $post_ids = array_keys( $pull_errors ); - - $_posts = $connection_now->remote_get( [ 'post__in' => $post_ids ] ); - $posts = []; - - if ( empty( $_posts ) ) { - return; - } - - foreach ( $_posts['items'] as $post ) { - $posts[ $post->ID ] = $post->post_title; - } - ?> - -
-

- -
- -