Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: Uninstall distributor #540

Open
wants to merge 60 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 57 commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
863ab95
feat: add uninstall script
dinhtungdu Mar 10, 2020
00b3768
feat: update uninstall script comment
dinhtungdu Mar 10, 2020
27d45b5
Merge branch 'develop' into fix/349
ravinderk Jul 20, 2023
b5e16da
Merge branch 'develop' into fix/349
ravinderk Aug 2, 2023
69ff81e
Merge branch 'develop' into fix/349
ravinderk Aug 4, 2023
c2fd377
format: add file doc tags
ravinderk Aug 4, 2023
620f8ef
fix: add prefix to transient name
ravinderk Aug 4, 2023
0f8eaec
refactor: remove unnecessary code
ravinderk Aug 4, 2023
07d1d3b
feat: flush cache
ravinderk Aug 4, 2023
f79234e
refactor: remove unnecessary check
ravinderk Aug 4, 2023
4d1dfad
format: improve code formatting
ravinderk Aug 4, 2023
fcd4299
format: disable phpcs for uninstall.php
ravinderk Aug 4, 2023
ab9e708
Merge branch 'develop' into fix/349
ravinderk Aug 4, 2023
1f5caf0
fix: define global variable
ravinderk Aug 4, 2023
f8b5fb9
feat: update last cache update date
ravinderk Aug 8, 2023
e62c579
feat: delete comment and term data
ravinderk Aug 8, 2023
74ceb30
fix: remove as from sql query
ravinderk Aug 8, 2023
034891e
fix: resolve sql query issue
ravinderk Aug 8, 2023
65a69b0
fix: delete only connections and options data
ravinderk Aug 8, 2023
3d02f1d
format: improve code formatting
ravinderk Aug 8, 2023
543be5e
feat: delete plugin data from post meta table
ravinderk Aug 9, 2023
910ff84
feat: merge sql query
ravinderk Aug 9, 2023
7c7856f
Merge branch 'develop' into fix/349
ravinderk Sep 8, 2023
12c1485
Merge branch 'develop' into fix/349
ravinderk Sep 22, 2023
bb7df21
feat: dispaly modal when click on plugin deactivaiton link
ravinderk Sep 22, 2023
0ede961
feat: update deacrtivation modal content
ravinderk Sep 22, 2023
71fdbd5
fix: deactivate modal should not display on sub sites
ravinderk Sep 25, 2023
d9b4b57
doc: update message in modal
ravinderk Sep 25, 2023
a149f14
format: remove extra line
ravinderk Sep 25, 2023
09f07b5
format: improve code formatting
ravinderk Sep 25, 2023
30d31b4
Merge branch 'develop' into fix/349
ravinderk Nov 16, 2023
76eb110
Merge branch 'develop' into fix/349
ravinderk Jan 9, 2024
8395719
Update uninstall.php
ravinderk Jan 15, 2024
d10a2f7
add: escape data for javascript
Jan 15, 2024
38c8d15
change: use function to regiter deaactivatin modal
Jan 15, 2024
fdae543
delete data for each sub-site when it is a network
faisal-alvi Jul 22, 2024
393ccdb
Delete only subscription data; keeping other posts w/meta
faisal-alvi Jul 25, 2024
2890ce9
Merge branch 'develop' into fix/349
faisal-alvi Jul 25, 2024
2506645
update the popup notice for more clarity
faisal-alvi Jul 25, 2024
45786f1
improve query; remove meta first and then posts (only subc)
faisal-alvi Jul 25, 2024
1188c05
remove unnec word
faisal-alvi Jul 25, 2024
27ab735
Merge branch 'develop' into fix/349
Sidsector9 Aug 6, 2024
2551260
Merge branch 'develop' into fix/349
Sidsector9 Aug 20, 2024
9854aed
Merge branch 'develop' into fix/349
Sidsector9 Aug 26, 2024
ff2b790
Merge branch 'develop' into fix/349
peterwilsoncc Sep 3, 2024
f2f9b73
suggestions implemented
faisal-alvi Sep 5, 2024
e0530b9
improvise the code
faisal-alvi Sep 5, 2024
b96e12a
coding standards
faisal-alvi Sep 5, 2024
ef103dd
add semicolon to help non-tech users
faisal-alvi Sep 5, 2024
7826ef2
fix the issue - options are not deleting in multisite network
faisal-alvi Sep 5, 2024
4f480a7
Merge branch 'develop' into fix/349
faisal-alvi Sep 6, 2024
6923237
Apply suggestions from code review
faisal-alvi Sep 6, 2024
15e060d
pass `option_name` in `wp_cache_delete_multiple` for `options`
faisal-alvi Sep 13, 2024
39830c6
pass `site_id:meta_key` in `wp_cache_delete_multiple` for `site-options`
faisal-alvi Sep 13, 2024
548c8a1
include site transient keys to delete
faisal-alvi Sep 13, 2024
2aa68df
phpcs: fixes and ignores
faisal-alvi Sep 13, 2024
8444087
Site transients can be used on single sites too.
faisal-alvi Sep 16, 2024
49aa191
Merge branch 'develop' into fix/349
faisal-alvi Oct 14, 2024
64621e9
Merge branch 'develop' into fix/349
faisal-alvi Oct 16, 2024
a46f0b5
include uninstall.php to release version
faisal-alvi Oct 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ tests/cypress/reports
tests/cypress/downloads

distributor.zip

.github/phpcs-report.xml
70 changes: 70 additions & 0 deletions includes/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

namespace Distributor;

use WP_Screen;
use YahnisElsts\PluginUpdateChecker\v5\PucFactory;

/**
Expand Down Expand Up @@ -216,6 +217,75 @@ function() {
1
);


/**
* Add a deactivation modal when deactivating the plugin.
*
* @since x.x.x
*/
function register_deactivation_modal() {
// Exit if deactivating plugin from sub site.
$screen = get_current_screen();
if ( ! ( ! is_multisite() || $screen->in_admin( 'network' ) ) ) {
return;
}

wp_enqueue_script( 'jquery-ui-dialog' );
wp_enqueue_style( 'wp-jquery-ui-dialog' );

add_action(
'admin_footer',
static function () {
printf(
'<div id="my-modal" style="display:none;"><p>%1$s</p><p>%2$s</p><p><code>%3$s</code></p><p>%4$s</p></div>',
esc_html__( 'Would you like to delete all Distributor data?', 'distributor' ),
esc_html__( 'By default, the database entries are not deleted when you deactivate Distributor. If you are deleting Distributor completely from your website and want those items removed as well, add the code below to wp-config.php:', 'distributor' ),
'define( \'DT_REMOVE_ALL_DATA\', true );',
esc_html__( 'After adding this code, the Distributor plugin data will be removed from the website database when deleting the plugin. This will not delete the posts with their metadata other than the subscription. You can review uninstall.php (in the plugin root directory) to learn more about the deleted data. After deleting the Distributor plugin, you can remove the code from the wp-config.php file. Please make sure that this action cannot be undone; take a backup before proceeding.', 'distributor' )
);
}
);

$modal_title = wp_json_encode( __( 'Distributor Deactivation', 'distributor' ) );
$modal_button_title_deactivate = wp_json_encode( __( 'Deactivate', 'distributor' ) );
$modal_button_title_cancel = wp_json_encode( __( 'Cancel', 'distributor' ) );
$script = <<<EOD
jQuery(document).ready(function($) {
const deactivateButton = jQuery('#deactivate-distributor');
deactivateButton.on( 'click', function() {
$("#my-modal").dialog({
modal: true,
title: $modal_title,
width: 550,
buttons: [
{
text: $modal_button_title_cancel,
class: "button-secondary",
click: function() {
$(this).dialog("close");
}
},
{
text:$modal_button_title_deactivate,
class: 'button-primary',
click: function() {
$(this).dialog("close");
window.location.assign(deactivateButton.attr('href'));
},
style: 'margin-left: 10px;'
}
]
});

return false;
});
});
EOD;

wp_add_inline_script( 'jquery-ui-dialog', $script );
}
add_action( 'load-plugins.php', __NAMESPACE__ . '\register_deactivation_modal' );

/**
* We use setup functions to avoid unit testing WP_Mock strict mode errors.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -861,7 +861,7 @@ public static function build_available_authorized_sites( $user_id = false, $cont
$last_changed = self::set_sites_last_changed_time();
}

$cache_key = "authorized_sites:$user_id:$context:$last_changed";
$cache_key = "dt_authorized_sites:$user_id:$context:$last_changed";
$authorized_sites = get_transient( $cache_key );

if ( $force || false === $authorized_sites ) {
Expand Down
205 changes: 205 additions & 0 deletions uninstall.php
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

File needs to be added to this block of code

patterns: [
{ from: 'readme.txt', to: './' },
{ from: 'README.md', to: './' },
{ from: 'CHANGELOG.md', to: './' },
{ from: 'composer.json', to: './' },
{ from: 'distributor.php', to: './' },
{ from: '.github/workflows/*', to: './' },
{ from: '.gitattributes', to: './' },
{ from: 'assets/img/*', to: './' },
{ from: 'dist/**/*', to: './' },
{ from: 'includes/**/*', to: './' },
{ from: 'lang/**/*', to: './' },
{ from: 'templates/**/*', to: './' },
{
from: 'vendor/yahnis-elsts/plugin-update-checker/**/*',
to: './',
},
],
} ),

Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
<?php
/**
* Distributor uninstall script.
*
* @package distributor
* @since x.x.x
*/

// phpcs:disable WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber

if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
die;
}

/*
* Only remove ALL data if DT_REMOVE_ALL_DATA constant is set to true in user's
* wp-config.php. This is to prevent data loss when deleting the plugin from the backend
* and to ensure only the site owner can perform this action.
*/
if ( defined( 'DT_REMOVE_ALL_DATA' ) && true === DT_REMOVE_ALL_DATA ) {
peterwilsoncc marked this conversation as resolved.
Show resolved Hide resolved
global $wpdb;

/**
* Function to delete all relevant data from the site.
*/
function dt_delete_data() {
global $wpdb;

// Delete post meta and posts of type 'dt_subscription'.
$subscription_post_ids = $wpdb->get_col(
$wpdb->prepare(
"SELECT ID FROM $wpdb->posts WHERE post_type = %s",
'dt_subscription'
)
);

if ( ! empty( $subscription_post_ids ) ) {
$ids_string = implode( ',', array_map( 'intval', $subscription_post_ids ) );

// Delete subscription meta.
$wpdb->query(
"DELETE FROM $wpdb->postmeta WHERE post_id IN ($ids_string)"
);

// Delete subscription posts.
$wpdb->query(
$wpdb->prepare(
"DELETE FROM $wpdb->posts WHERE ID IN (%s)",
$ids_string
)
);

// Clear the post cache.
wp_cache_set_posts_last_changed();
wp_cache_delete_multiple( $subscription_post_ids, 'posts' );
wp_cache_delete_multiple( $subscription_post_ids, 'post_meta' );
}

// Delete relevant options from the options table.
delete_site_options();
}

/**
* Delete all relevant options from the options table.
*/
function delete_site_options() {
global $wpdb;

$option_prefixes = array(
'dt_',
'_transient_dt_',
'_transient_timeout_dt_',
);

peterwilsoncc marked this conversation as resolved.
Show resolved Hide resolved
// Site transients can be used on single sites too.
// See https://github.com/10up/distributor/pull/540#discussion_r1759587692
if ( ! is_multisite() ) {
$option_prefixes = array_merge(
$option_prefixes,
array(
'_site_transient_dt_',
'_site_transient_timeout_dt_',
)
);
}

// Prepare the WHERE clause for the options table.
$where_clause = implode( ' OR ', array_fill( 0, count( $option_prefixes ), 'option_name LIKE %s' ) );

// Prepare the query.
$query = $wpdb->prepare(
sprintf(
"SELECT option_id, option_name FROM $wpdb->options WHERE %s;",
$where_clause
),
array_map(
function( $prefix ) use ( $wpdb ) {
return $wpdb->esc_like( $prefix ) . '%';
},
$option_prefixes
)
);

// Fetch the options to delete.
$options_to_delete = $wpdb->get_results( $query, ARRAY_A );

if ( ! empty( $options_to_delete ) ) {
// Collect IDs from fetched options.
$ids = array_column( $options_to_delete, 'option_id' );
$ids_string = implode( ',', array_map( 'intval', $ids ) );

// Delete the options using the retrieved IDs.
$wpdb->query(
$wpdb->prepare(
"DELETE FROM $wpdb->options WHERE option_id IN ($ids_string)"
)
);

// Flush the options cache.
$option_names = array_column( $options_to_delete, 'option_name' );
wp_cache_delete_multiple( $option_names, 'options' );

// Flush the alloptions cache.
wp_cache_delete( 'alloptions', 'options' );
}
}

/**
* Delete all relevant options from the sitemeta table (multisite only).
*/
function delete_sitemeta_options() {
global $wpdb;

$option_prefixes = array(
'dt_',
'_site_transient_dt_',
'_site_transient_timeout_dt_',
);

// Prepare the WHERE clause for the sitemeta table.
$where_clause = implode( ' OR ', array_fill( 0, count( $option_prefixes ), 'meta_key LIKE %s' ) );

$site_id = get_current_network_id();

$query = $wpdb->prepare(
sprintf(
"SELECT meta_id, meta_key FROM $wpdb->sitemeta WHERE site_id = %%d AND (%s);",
$where_clause
),
array_merge(
[ $site_id ],
array_map(
function( $prefix ) use ( $wpdb ) {
return $wpdb->esc_like( $prefix ) . '%';
},
$option_prefixes
)
)
);

// Fetch the sitemeta to delete.
$sitemeta_to_delete = $wpdb->get_results( $query, ARRAY_A );

if ( ! empty( $sitemeta_to_delete ) ) {
// Collect IDs from fetched options.
$ids = array_column( $sitemeta_to_delete, 'meta_id' );
$ids_string = implode( ',', array_map( 'intval', $ids ) );

// Delete the sitemeta using the retrieved IDs.
$wpdb->query(
$wpdb->prepare(
"DELETE FROM $wpdb->sitemeta WHERE meta_id IN ($ids_string)"
)
);

// Flush the site options cache.
$key_names = array_column( $sitemeta_to_delete, 'meta_key' );
$key_names = array_map(
function( $key ) use ( $site_id ) {
return $site_id . ':' . $key;
},
$key_names
);
wp_cache_delete_multiple( $key_names, 'site-options' );
}
}

// Check if it's a multisite installation.
if ( is_multisite() ) {
// Loop through each site in the network.
$sites = get_sites();
foreach ( $sites as $site ) {
switch_to_blog( $site->blog_id );
dt_delete_data();
restore_current_blog();
}

// Delete network-wide sitemeta options.
delete_sitemeta_options();
} else {
// Single site.
dt_delete_data();
}
}

Loading