Skip to content
This repository was archived by the owner on Dec 27, 2022. It is now read-only.

Commit 59b8b66

Browse files
authored
Merge pull request #92 from xwp/feature/snapshot-merge
Add ability to merge multiple snapshots into a new snapshot
2 parents 9625866 + f2cefad commit 59b8b66

File tree

3 files changed

+285
-1
lines changed

3 files changed

+285
-1
lines changed

dev-lib

php/class-post-type.php

+147
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,16 @@ public function register() {
115115
add_action( 'admin_notices', array( $this, 'show_publish_error_admin_notice' ) );
116116
add_action( 'post_submitbox_minor_actions', array( $this, 'hide_disabled_publishing_actions' ) );
117117
add_action( 'admin_print_scripts-revision.php', array( $this, 'disable_revision_ui_for_published_posts' ) );
118+
119+
// Version check for bulk action.
120+
if ( version_compare( get_bloginfo( 'version' ), '4.7', '>=' ) ) {
121+
add_filter( 'bulk_actions-edit-' . self::SLUG, array( $this, 'add_snapshot_bulk_actions' ) );
122+
add_filter( 'handle_bulk_actions-edit-' . self::SLUG, array( $this, 'handle_snapshot_bulk_actions' ), 10, 3 );
123+
} else {
124+
add_action( 'admin_print_footer_scripts-edit.php', array( $this, 'snapshot_merge_print_script' ) );
125+
add_action( 'load-edit.php', array( $this, 'handle_snapshot_bulk_actions_workaround' ) );
126+
}
127+
add_action( 'admin_notices', array( $this, 'admin_show_merge_error' ) );
118128
}
119129

120130
/**
@@ -675,4 +685,141 @@ public function hide_disabled_publishing_actions( $post ) {
675685
</style>
676686
<?php
677687
}
688+
689+
/**
690+
* Add snapshot bulk actions.
691+
*
692+
* @param array $bulk_actions actions.
693+
*
694+
* @return mixed
695+
*/
696+
public function add_snapshot_bulk_actions( $bulk_actions ) {
697+
$bulk_actions['merge_snapshot'] = __( 'Merge Snapshot', 'customize-snapshots' );
698+
return $bulk_actions;
699+
}
700+
701+
/**
702+
* Handle bulk actions.
703+
*
704+
* @param string $redirect_to url to redirect to.
705+
* @param string $do_action current action.
706+
* @param array $post_ids post ids.
707+
*
708+
* @return string url.
709+
*/
710+
public function handle_snapshot_bulk_actions( $redirect_to, $do_action, $post_ids ) {
711+
if ( 'merge_snapshot' !== $do_action ) {
712+
return $redirect_to;
713+
}
714+
$posts = array_map( 'get_post', $post_ids );
715+
if ( count( $posts ) <= 1 ) {
716+
return empty( $redirect_to ) ? add_query_arg( array( 'merge-error' => 1 ) ) : add_query_arg( array( 'merge-error' => 1 ), $redirect_to );
717+
}
718+
719+
usort( $posts, function( $a, $b ) {
720+
$compare_a = $a->post_modified;
721+
$compare_b = $b->post_modified;
722+
if ( '0000-00-00 00:00:00' === $compare_a ) {
723+
$compare_a = $a->post_date;
724+
}
725+
if ( '0000-00-00 00:00:00' === $compare_b ) {
726+
$compare_b = $b->post_date;
727+
}
728+
return strtotime( $compare_a ) - strtotime( $compare_b );
729+
} );
730+
731+
$snapshot_post_data = array();
732+
foreach ( $posts as $post ) {
733+
$snapshot_post_data[] = array(
734+
'data' => $this->get_post_content( $post ),
735+
'uuid' => $post->post_name,
736+
);
737+
}
738+
$snapshots_data = wp_list_pluck( $snapshot_post_data, 'data' );
739+
$conflict_keys = call_user_func_array( 'array_intersect_key', $snapshots_data );
740+
$merged_snapshot_data = call_user_func_array( 'array_merge', $snapshots_data );
741+
742+
foreach ( $conflict_keys as $key => $conflict_val ) {
743+
$original_values = array();
744+
foreach ( $snapshot_post_data as $post_data ) {
745+
if ( isset( $post_data['data'][ $key ] ) ) {
746+
$original_values[] = array(
747+
'uuid' => $post_data['uuid'],
748+
'value' => $post_data['data'][ $key ]['value'],
749+
);
750+
}
751+
}
752+
$merged_snapshot_data[ $key ]['merge_conflict'] = $original_values;
753+
}
754+
$post_id = $this->save( array(
755+
'uuid' => Customize_Snapshot_Manager::generate_uuid(),
756+
'status' => 'draft',
757+
'data' => $merged_snapshot_data,
758+
'post_date' => current_time( 'mysql', false ),
759+
'post_date_gmt' => current_time( 'mysql', true ),
760+
) );
761+
$redirect_to = get_edit_post_link( $post_id, 'raw' );
762+
return $redirect_to;
763+
}
764+
765+
/**
766+
* Insert script for adding merge snapshot bulk action polyfill.
767+
*/
768+
public function snapshot_merge_print_script() {
769+
global $post_type;
770+
if ( self::SLUG === $post_type ) {
771+
?>
772+
<script type="text/javascript">
773+
jQuery( function( $ ) {
774+
var optionText = <?php echo wp_json_encode( __( 'Merge Snapshot', 'customize-snapshots' ) ); ?>;
775+
$( 'select[name="action"], select[name="action2"]' ).each( function() {
776+
var option = $( '<option>', {
777+
text: optionText,
778+
value: 'merge_snapshot'
779+
} );
780+
$( this ).append( option );
781+
} );
782+
} );
783+
</script>
784+
<?php
785+
}
786+
}
787+
788+
/**
789+
* Handles bulk action for 4.6.x and older version.
790+
*/
791+
public function handle_snapshot_bulk_actions_workaround() {
792+
$wp_list_table = _get_list_table( 'WP_Posts_List_Table' );
793+
$action = $wp_list_table->current_action();
794+
if ( 'merge_snapshot' !== $action || ( isset( $_REQUEST['post_type'] ) && self::SLUG !== wp_unslash( $_REQUEST['post_type'] ) ) ) {
795+
return;
796+
}
797+
check_admin_referer( 'bulk-posts' );
798+
$post_ids = array_map( 'intval', $_REQUEST['post'] );
799+
if ( empty( $post_ids ) ) {
800+
return;
801+
}
802+
$redirect_url = $this->handle_snapshot_bulk_actions( wp_get_referer(), 'merge_snapshot', $post_ids );
803+
if ( ! empty( $redirect_url ) ) {
804+
wp_safe_redirect( $redirect_url );
805+
exit;
806+
}
807+
}
808+
809+
/**
810+
* Show admin notice in case of merge error
811+
*/
812+
public function admin_show_merge_error() {
813+
if ( ! isset( $_REQUEST['merge-error'] ) ) {
814+
return;
815+
}
816+
$error = array(
817+
1 => __( 'At-least two snapshot required for merge.', 'customize-snapshots' ),
818+
);
819+
$error_code = intval( $_REQUEST['merge-error'] );
820+
if ( ! isset( $error[ $error_code ] ) ) {
821+
return;
822+
}
823+
printf( '<div class="notice notice-error is-dismissible"><p>%s</p></div>', esc_html( $error[ $error_code ] ) );
824+
}
678825
}

tests/php/test-class-post-type.php

+137
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,15 @@ public function test_register() {
5858
$this->assertEquals( 10, has_action( 'transition_post_status', array( $post_type->snapshot_manager, 'save_settings_with_publish_snapshot' ) ) );
5959
$this->assertEquals( 10, has_filter( 'wp_insert_post_data', array( $post_type->snapshot_manager, 'prepare_snapshot_post_content_for_publish' ) ) );
6060
$this->assertEquals( 10, has_action( 'display_post_states', array( $post_type, 'display_post_states' ) ) );
61+
62+
if ( version_compare( get_bloginfo( 'version' ), '4.7', '>=' ) ) {
63+
$this->assertEquals( 10, has_filter( 'bulk_actions-edit-' . Post_Type::SLUG, array( $post_type, 'add_snapshot_bulk_actions' ) ) );
64+
$this->assertEquals( 10, has_filter( 'handle_bulk_actions-edit-' . Post_Type::SLUG, array( $post_type, 'handle_snapshot_bulk_actions' ) ) );
65+
} else {
66+
$this->assertEquals( 10, has_action( 'admin_print_footer_scripts-edit.php', array( $post_type, 'snapshot_merge_print_script' ) ) );
67+
$this->assertEquals( 10, has_action( 'load-edit.php', array( $post_type, 'handle_snapshot_bulk_actions_workaround' ) ) );
68+
}
69+
$this->assertEquals( 10, has_action( 'admin_notices', array( $post_type, 'admin_show_merge_error' ) ) );
6170
}
6271

6372
/**
@@ -749,4 +758,132 @@ public function test_hide_disabled_publishing_actions() {
749758
$this->assertNotEmpty( $output );
750759
$this->assertContains( 'misc-pub-post-status', $output );
751760
}
761+
762+
/**
763+
* Tests add_snapshot_bulk_actions
764+
*
765+
* @see Post_Type::add_snapshot_bulk_actions()
766+
*/
767+
public function test_add_snapshot_bulk_actions() {
768+
$post_type = new Post_Type( $this->plugin->customize_snapshot_manager );
769+
$data = $post_type->add_snapshot_bulk_actions( array() );
770+
$this->assertArrayHasKey( 'merge_snapshot', $data );
771+
}
772+
773+
/**
774+
* Test handle_snapshot_bulk_actions
775+
*
776+
* @see Post_Type::handle_snapshot_bulk_actions()
777+
*/
778+
public function test_handle_snapshot_bulk_actions() {
779+
$post_type = new Post_Type( $this->plugin->customize_snapshot_manager );
780+
$date1 = gmdate( 'Y-m-d H:i:s', ( time() + ( get_option( 'gmt_offset' ) * HOUR_IN_SECONDS ) ) );
781+
$post_1 = $post_type->save( array(
782+
'uuid' => Customize_Snapshot_Manager::generate_uuid(),
783+
'status' => 'draft',
784+
'data' => array(
785+
'foo' => array(
786+
'value' => 'bar',
787+
),
788+
),
789+
'post_date' => $date1,
790+
'post_date_gmt' => $date1,
791+
'edit_date' => $date1,
792+
) );
793+
$value = array(
794+
'foo' => array(
795+
'value' => 'baz',
796+
),
797+
'baz' => array(
798+
'value' => 'zab',
799+
),
800+
);
801+
$date2 = gmdate( 'Y-m-d H:i:s', ( time() + 60 + ( get_option( 'gmt_offset' ) * HOUR_IN_SECONDS ) ) );
802+
$post_2 = $post_type->save( array(
803+
'uuid' => Customize_Snapshot_Manager::generate_uuid(),
804+
'status' => 'draft',
805+
'data' => $value,
806+
'post_date' => $date2,
807+
'post_date_gmt' => $date2,
808+
'edit_date' => $date2,
809+
) );
810+
811+
$post_type->handle_snapshot_bulk_actions( '', 'merge_snapshot', array( $post_1, $post_2 ) );
812+
$merged_post = get_post( $post_2 + 1 );
813+
$value['foo']['merge_conflict'] = array(
814+
array(
815+
'uuid' => get_post( $post_1 )->post_name,
816+
'value' => 'bar',
817+
),
818+
array(
819+
'uuid' => get_post( $post_2 )->post_name,
820+
'value' => 'baz',
821+
),
822+
);
823+
$this->assertSame( $value, $post_type->get_post_content( $merged_post ) );
824+
}
825+
826+
/**
827+
* Test snapshot_merge_print_script
828+
*
829+
* @see Post_Type::snapshot_merge_print_script()
830+
*/
831+
public function test_snapshot_merge_print_script() {
832+
global $post_type;
833+
$post_type = Post_Type::SLUG; // WPCS: global override ok.
834+
$post_type_obj = new Post_Type( $this->plugin->customize_snapshot_manager );
835+
ob_start();
836+
$post_type_obj->snapshot_merge_print_script();
837+
$script_content = ob_get_clean();
838+
839+
$this->assertContains( 'select[name="action"]', $script_content );
840+
$this->assertContains( 'select[name="action2"]', $script_content );
841+
$this->assertContains( 'merge_snapshot', $script_content );
842+
$this->assertContains( 'text/javascript', $script_content );
843+
}
844+
845+
/**
846+
* Test handle_snapshot_bulk_actions_workaround
847+
*
848+
* @see Post_Type::handle_snapshot_bulk_actions_workaround()
849+
*/
850+
public function test_handle_snapshot_bulk_actions_workaround() {
851+
$GLOBALS['hook_suffix'] = 'posts-' . Post_Type::SLUG; // WPCS: global override ok.
852+
$_POST['action'] = $_REQUEST['action'] = $_GET['action'] = 'merge_snapshot';
853+
$_POST['post_type'] = $_REQUEST['post_type'] = $_GET['post_type'] = Post_Type::SLUG;
854+
$_POST['post'] = $_REQUEST['post'] = $_GET['post'] = array( 1, 2 );
855+
$_POST['_wpnonce'] = $_REQUEST['_wpnonce'] = $_GET['_wpnonce'] = wp_create_nonce( 'bulk-posts' );
856+
$_POST['_wp_http_referer'] = $_REQUEST['_wp_http_referer'] = $_GET['_wp_http_referer'] = admin_url();
857+
$post_type_obj = $this->getMockBuilder( 'CustomizeSnapshots\Post_Type' )
858+
->setConstructorArgs( array( $this->plugin->customize_snapshot_manager ) )
859+
->setMethods( array( 'handle_snapshot_bulk_actions' ) )
860+
->getMock();
861+
$post_type_obj->expects( $this->once() )
862+
->method( 'handle_snapshot_bulk_actions' )
863+
->will( $this->returnValue( null ) );
864+
$post_type_obj->handle_snapshot_bulk_actions_workaround();
865+
}
866+
867+
/**
868+
* Test admin_show_merge_error
869+
*
870+
* @see Post_Type::admin_show_merge_error()
871+
*/
872+
public function test_admin_show_merge_error() {
873+
$post_type_obj = new Post_Type( $this->plugin->customize_snapshot_manager );
874+
ob_start();
875+
$post_type_obj->admin_show_merge_error();
876+
$notice_content = ob_get_clean();
877+
$this->assertEmpty( $notice_content );
878+
ob_start();
879+
$_POST['merge-error'] = $_REQUEST['merge-error'] = $_GET['merge-error'] = 1;
880+
$post_type_obj->admin_show_merge_error();
881+
$notice_content = ob_get_clean();
882+
$this->assertContains( 'notice-error', $notice_content );
883+
$_POST['merge-error'] = $_REQUEST['merge-error'] = $_GET['merge-error'] = 5;
884+
ob_start();
885+
$post_type_obj->admin_show_merge_error();
886+
$notice_content = ob_get_clean();
887+
$this->assertEmpty( $notice_content );
888+
}
752889
}

0 commit comments

Comments
 (0)