Ok, I did some research/testing on it an in short terms:
Don't use uppercase in the first place!
I've currently noticed only one problem where the ajax based update from the plugins overview (introduced in WP 4.2) throws a JS error but it may be a problem in the future.
This is because the ajax response uses sanitize_key
in the wp_ajax_update_plugin
method (browse in trac).
The sanitize_key
method does next to sanitization a strtolower
which causes the "myPlugin
" gets a "myplugin
".
The updateSuccess
method tries now to update the row where the slug is "myplugin
" instead of "myPlugin
".
This could be prevented by changing line 694 in wp-admin/includes/class-wp-plugins-list-table.php
from
printf( "<tr id='%s' class='%s' data-slug='%s'>",
$id,
$class,
$plugin_slug
);
to
printf( "<tr id='%s' class='%s' data-slug='%s'>",
$id,
$class,
sanitize_key($plugin_slug)
);
todo: open ticket Ticket #35032
To answer the initial question
You can change the slug on a plugin update but you have to take care of following:
- the following code must be included in the old version (eg.
`myPlugin/myPlugin.php)
- the updated version must have a slug of `myplugin/myplugin.php
- don't use
delete_plugin()
which may trigger an uninstall.php and removes plugins data
Here's the code you can start with:
//hook into when new plugin has been successfully updated
add_filter( 'upgrader_post_install', 'my_move_plugin_slug', 10 ,3 );
function my_move_plugin_slug($should_be_true, $hook_extra, $result){
global $wpdb, $wp_filesystem;
$from = 'myPlugin/myPlugin.php';
$to = 'myplugin/myplugin.php';
//some general checks
if(!$should_be_true || !isset($hook_extra['plugin']) || $hook_extra['plugin'] != $to) return $should_be_true;
$old_destination = $result['local_destination'].'/'.dirname($from).'/';
//old location doesn't exist (anymore)
if(!is_dir($old_destination)) return $should_be_true;
//new location is the same as the old one
if($old_destination == $result['destination']) return $should_be_true;
//do the magic
//rewrite location in the database
echo '<p>Moving the plugin to new location… ';
if(false !== $wpdb->query($wpdb->prepare("UPDATE {$wpdb->options} SET `option_value` = replace(option_value, %s, %s)", $from, $to))){
echo '<strong>done</strong>';
}
echo '</p>';
//delete folder
echo '<p>Removing the old directory of the plugin… ';
if($wp_filesystem->delete( $old_destination , true )){
echo '<strong>done</strong>';
}
echo '</p>';
}
Second method
If you have a dedicate batch update progress in you plugin like I have you can use this code which is in my opinion a bit safer as it's triggered by explicit user interaction and doesn't require wp_filesystem:
function do_change_plugin_slug(){
global $wpdb;
$from = 'myMail/myMail.php';
$to = 'mymail/mymail.php';
$old_destination = WP_PLUGIN_DIR.'/'.$from;
$new_destination = WP_PLUGIN_DIR.'/'.$to;
//old location doesn't exist (anymore)
if(!file_exists($old_destination)) return true;
//new location is the same as the old one
if($old_destination == $new_destination) return true;
//do the magic
echo 'Removing the old file of the plugin… ';
if(rename( $old_destination , dirname($old_destination).'/'.basename($new_destination) )){
echo 'done';
}else{
echo 'failed';
}
echo "\n";
echo 'Removing the old directory of the plugin… ';
if(rename( dirname($old_destination) , dirname($new_destination) )){
echo 'done';
}else{
echo 'failed';
}
echo "\n";
//rewrite location in the database
echo 'Moving the plugin to new location… ';
if(false !== $wpdb->query($wpdb->prepare("UPDATE {$wpdb->options} SET `option_value` = replace(option_value, %s, %s)", $from, $to))){
echo 'done';
}else{
echo 'failed';
}
echo "\n";
return true;
}
In the end you should always make sure the user understands what's going on and/or why this is a necessary step.
slug
- which is always lowercase - to check if version is up to date, does exits etc. These checks are not required but disturb the UX. I know it will be disabled - which is the only problem - but how can I enable a "foreign" plugin?