PluginProbe
PlugVersions – Easily roll back to previous versions of your plugins. / 0.1.0
PlugVersions – Easily roll back to previous versions of your plugins. v0.1.0
0.0.6.RC-1 0.0.7 0.0.8 0.1.0 0.2.0 0.2.1 trunk 0.0.1 0.0.2 0.0.3 0.0.4 0.0.5 0.0.6
plugversions / admin / pr-admin.php

pr-admin.php in PlugVersions – Easily roll back to previous versions of your plugins. 0.1.0, at admin/pr-admin.php

398 lines 12.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * It includes the code for the backend.
4
5 * @package Plugversions
6 */
7 defined( 'PLUGIN_REVISIONS_PLUGIN_DIR' ) || exit; // Exit if not accessed from Plugversions.
8
9 if( wp_doing_ajax() ){
10 //* Include the Ajax admin file.
11 require_once PLUGIN_REVISIONS_PLUGIN_DIR . '/admin/pr-ajax-admin.php';
12 }
13
14 register_activation_hook( __FILE__, function() {
15 /**
16 * Actions triggered after plugin activation or after a new site of a multisite installation is created
17 *
18 * @since 0.0.1
19 */
20 if( ! eos_plugin_revision_key() ){
21 update_site_option( 'plugin_revisions',array( 'time' => time() ) );
22 }
23 } );
24
25 add_action( 'admin_head',function(){
26 /**
27 * Add style to properly show the revisions on the page of plugins
28 *
29 * @since 0.0.1
30 */
31 global $pagenow;
32 if( $pagenow && 'plugins.php' === sanitize_text_field( $pagenow ) ){
33 ?>
34 <style id="plugin-revisions-css" type="text/css">
35 .plugin-revision-wrp{
36 position:relative
37 }
38 .plugin-revisions-vers{
39 display:none;
40 position:absolute;
41 <?php echo is_rtl() ? 'right' : 'left'; ?>:0;
42 top:0
43 }
44 .plugin-revision-wrp:hover .plugin-revisions-vers{
45 display:block;
46 min-width:200px;
47 min-width:max-content;
48 background:#fff;
49 margin-top:15px;
50 padding:10px 10px;
51 z-index:9
52 }
53 .plugin-revision-wrp:hover .plugin-revisions-vers a{
54 display:block;
55 border-bottom:1px dashed;
56 margin-bottom:5px;
57 background-image:url(<?php echo esc_url( PLUGIN_REVISIONS_PLUGIN_URL ); ?>/admin/assets/images/ajax-loader.gif );
58 background-position:-9999px -9999px;
59 background-repeat:no-repeat;
60 background-size:16px 16px
61 }
62 </style>
63 <?php
64 }
65 } );
66
67 add_action( 'admin_footer',function(){
68 /**
69 * Add JS to restore a revision via Ajax
70 * This script is added to the plugins.php and plugin-editor.php pages.
71 *
72 * @since 0.0.1
73 */
74 global $pagenow;
75 if( $pagenow && 'plugins.php' === sanitize_text_field( $pagenow ) ){
76 wp_nonce_field( 'plugin_reviews_restore_version','plugin_reviews_restore_version' );
77 ?>
78 <script id="plugin-revisions-js">
79 function eos_plugin_revisions(){
80 var as = document.getElementsByClassName('plugin-revision-action'),n=0,req = new XMLHttpRequest(),fd=new FormData(),nonce=document.getElementById('plugin_reviews_restore_version').value;
81 for(n;n<as.length;++n){
82 as[n].addEventListener('click',function(e){
83 e.preventDefault();
84 this.style.backgroundPosition = 'center center';
85 for(var k=0;k<as.length;++k){
86 as[k].style.pointerEvents = 'none';
87 }
88 fd.append("dir",this.dataset.dir);
89 fd.append("parent_plugin",this.dataset.parent_plugin);
90 fd.append("nonce",nonce);
91 req.open("POST","<?php echo esc_js( admin_url( 'admin-ajax.php' ) ); ?>" + '?action=eos_plugin_reviews_restore_version',true);
92 req.send(fd);
93 return false;
94 });
95 }
96 req.onload = function(e) {
97 for(n;n<as.length;++n){
98 as[n].style.backgroundPosition = '-9999px -9999px';
99 }
100 if(this.readyState === 4) {
101 window.location.reload();
102 }
103 else{
104 alert('Something went wrong!');
105 }
106 return false;
107 };
108 return false;
109 }
110 eos_plugin_revisions();
111 </script>
112 <?php
113 }
114 if( $pagenow && 'plugin-editor.php' === sanitize_text_field( $pagenow ) ){
115 $key = eos_plugin_revision_key();
116 ?>
117 <script id="plugin-revisions-file-editor-js">
118 function eos_plugin_revisions_fild_editor(){
119 var os=document.getElementById('plugin').getElementsByTagName('option'),n=0,ver='';
120 for(n;n<os.length;++n){
121 if(os[n].value.indexOf("<?php echo esc_js( $key ); ?>") > 0){
122 os[n].innerHTML += ' ' + os[n].value.split('-ver-')[0].split('-')[2];
123 }
124 }
125 }
126 eos_plugin_revisions_fild_editor();
127 </script>
128 <?php
129 }
130 } );
131
132 /**
133 * Remove all plugin revisions
134 * This function removes all revisions of a specific plugin or all plugins if no plugin name is provided.
135 *
136 * @since 0.0.1
137 */
138 function eos_plugin_revisions_remove_versions( $N = false,$plugin_name = false ){
139 $key = eos_plugin_revision_key();
140 $all_dirs = eos_plugin_revisions_scandir( dirname( PLUGIN_REVISIONS_PLUGIN_DIR ) );
141 global $wp_filesystem;
142 if( empty( $wp_filesystem ) ){
143 require_once ABSPATH . 'wp-admin/includes/file.php';
144 WP_Filesystem();
145 }
146 if( $all_dirs && !empty( $all_dirs ) ){
147 foreach( $all_dirs as $all_dir ){
148 if( substr( $all_dir,-strlen( $plugin_name ),strlen( $plugin_name ) ) !== $plugin_name ){
149 unset( $all_dirs[array_search( $all_dir,$all_dirs )] );
150 }
151 }
152 $n = 0;
153 foreach( $all_dirs as $dir ){
154 if( false !== strpos( $dir,'pr-'.$key.'-' ) ){
155 if( $N && $n < ( count( $all_dirs ) - absint( $N ) ) ){
156 $result = $wp_filesystem->delete( dirname( PLUGIN_REVISIONS_PLUGIN_DIR ).'/'.$dir,true );
157 }
158 }
159 ++$n;
160 }
161 }
162 }
163
164 /**
165 * Remove all revisions of all the plugins
166 *
167 * @since 0.0.1
168 */
169 function eos_plugin_revisions_remove_all_versions(){
170 $key = eos_plugin_revision_key();
171 $all_dirs = eos_plugin_revisions_scandir( dirname( PLUGIN_REVISIONS_PLUGIN_DIR ) );
172 global $wp_filesystem;
173 if( empty( $wp_filesystem ) ){
174 require_once ABSPATH . 'wp-admin/includes/file.php';
175 WP_Filesystem();
176 }
177 if( $all_dirs && !empty( $all_dirs ) ){
178 foreach( $all_dirs as $dir ){
179 if( false !== strpos( $dir,'pr-'.$key.'-' ) ){
180 $result = $wp_filesystem->delete( dirname( PLUGIN_REVISIONS_PLUGIN_DIR ).'/'.$dir,true );
181 }
182 }
183 }
184 }
185
186 /**
187 * Revisions scandir
188 * This function scans the plugin revisions directory and returns an array of files sorted by modification time.
189 *
190 * @since 0.0.1
191 */
192 function eos_plugin_revisions_scandir( $dir ) {
193 $ignored = array('.', '..', '.svn', '.htaccess');
194 $files = array();
195 foreach( scandir( $dir ) as $file ){
196 if( in_array( $file,$ignored ) ) continue;
197 $files[$file] = filemtime( $dir.'/'.$file );
198 }
199 asort( $files );
200 $files = array_keys( $files );
201 return ( $files ) ? $files : false;
202 }
203
204 add_action( 'admin_init', 'eos_pv_restore_revision_links' );
205 /**
206 * Add links to restore previous revisions from zipped plugins.
207 * This function initializes the restoring link functionality for plugin revisions.
208 *
209 * @since 0.0.6
210 */
211 function eos_pv_restore_revision_links() {
212 require_once PLUGIN_REVISIONS_PLUGIN_DIR . '/admin/classes/class-plugversions-restoring-link.php';
213 $link = new PlugVersions_Restoring_Link();
214 }
215
216
217
218 add_filter(
219 'upgrader_package_options',
220 function ( $options ) {
221 //*
222 //* Update revisions after plugin update
223 //* This filter is used to create a backup of the plugin before it is updated.
224 //*
225 //* @since 0.0.1
226 //* //
227 if ( isset( $options['destination'] ) && isset( $options['hook_extra'] ) ) {
228 $hook_extra = $options['hook_extra'];
229 if ( isset( $hook_extra['plugin'] ) ) {
230 $plugin = $hook_extra['plugin'];
231 $plugin_name = dirname( $plugin );
232 $path = sanitize_option( 'upload_path', $options['destination'] . '/' . $plugin );
233 if ( file_exists( $path ) ) {
234 $plugin_data = get_plugin_data( $path );
235 $version = $plugin_data['Version'];
236 if ( ! class_exists( 'WP_Upgrader' ) ) {
237 if ( file_exists( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' ) ) {
238 require_once ABSPATH . '/wp-admin/includes/class-wp-upgrader.php';
239 }
240 }
241 if ( class_exists( 'WP_Upgrader' ) ) {
242 $upgrader = new WP_Upgrader();
243 $key = eos_plugin_revision_key();
244 if ( $key ) {
245 $zip_dirname = 'pr-' . $key . '-' . sanitize_option( 'upload_path', $version ) . '-ver-' . dirname( $plugin );
246 $destination = str_replace( dirname( $plugin ), $zip_dirname, sanitize_option( 'upload_path', plugin_dir_path( $path ) ) );
247 if ( ! is_dir( $destination ) ) {
248 global $wp_filesystem;
249 if ( empty( $wp_filesystem ) ) {
250 require_once ABSPATH . '/wp-admin/includes/file.php';
251 WP_Filesystem();
252 }
253 $wp_filesystem->mkdir( $destination );
254 }
255 if ( ! empty( $wp_filesystem ) && $wp_filesystem->is_dir( $destination ) ) {
256 $result = copy_dir( plugin_dir_path( $path ), $destination );
257 $zip_dirname .= '.zip';
258 if ( eos_pv_create_zip_pclzip( $destination, $zip_dirname ) ) {
259 // If zipped archive created delete the unzipped directory.
260 $wp_filesystem->delete( $destination, true );
261 }
262 eos_plugin_revisions_remove_versions( apply_filters( 'max_plugin_revisions', 4 ), $plugin_name );
263 }
264 }
265 }
266 }
267 }
268 }
269 return $options;
270 },
271 10,
272 4
273 );
274
275
276 /**
277 * Backup the plugin before update.
278 * This filter allows you to catch all possible update scenarios, including bulk and manual replacements, as well as Ajax updates.
279 *
280 * @param string $source Plugin source path.
281 * @param string $remote_source Plugin remote source path.
282 * @param object $upgrader Upgrader object.
283 * @param array $hook_extra Hook extra data.
284 * @return string
285 *
286 * @author Vincenzo Casu.
287 */
288 function eos_plugin_unversal_backup( $source, $remote_source, $upgrader, $hook_extra ) {
289 // error_log( 'HOOK_EXTRA: ' . json_encode( $hook_extra ) );
290
291 // Load get_plugin_data() if not exists.
292 if ( ! function_exists( 'get_plugin_data' ) ) {
293 require_once ABSPATH . 'wp-admin/includes/plugin.php';
294 }
295
296 // Get plugins to backup.
297 $plugins_to_backup = array();
298
299 if ( ! empty( $hook_extra['plugins'] ) && is_array( $hook_extra['plugins'] ) ) {
300 // Bulk update (hook_extra['plugins']).
301 // error_log( 'Bulk update plugins: ' . json_encode( $hook_extra['plugins'] ) );
302 $plugins_to_backup = $hook_extra['plugins'];
303 } elseif ( ! empty( $hook_extra['plugin'] ) ) {
304 // Single AJAX update (hook_extra['plugin']).
305 // error_log( 'Single plugin update: ' . $hook_extra['plugin'] );
306 $plugins_to_backup = array( $hook_extra['plugin'] );
307 } elseif ( ! empty( $hook_extra['type'] ) && $hook_extra['type'] === 'plugin'
308 && ! empty( $hook_extra['action'] )
309 && in_array( $hook_extra['action'], array( 'update', 'install' ), true ) ) {
310 // Fallback “replace from ZIP” or other plugin-based cases.
311
312 // Get slug from source.
313 $slug = basename( $source );
314 // error_log( 'Fallback slug: ' . $slug );
315
316 $all = get_plugins( '/' . $slug );
317 // error_log( 'Found via get_plugins: ' . json_encode( array_keys( $all ) ) );
318
319 if ( ! empty( $all ) ) {
320 $main_file = key( $all );
321 $plugins_to_backup[] = $slug . '/' . $main_file;
322 } else {
323 // error_log( 'No plugin data for slug ' . $slug );
324 return $source;
325 }
326 } else {
327 // error_log( 'Nothing to backup, return.' );
328 return $source;
329 }
330
331 // Preparing Filesystem & Upgrader.
332 if ( ! class_exists( 'WP_Upgrader' ) ) {
333 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
334 }
335
336 global $wp_filesystem;
337 if ( empty( $wp_filesystem ) ) {
338 require_once ABSPATH . 'wp-admin/includes/file.php';
339 WP_Filesystem();
340 }
341
342 $key = eos_plugin_revision_key();
343 // error_log( 'Revision key: ' . $key );
344 if ( ! $key ) {
345 return $source;
346 }
347
348 foreach ( $plugins_to_backup as $plugin_file ) {
349 // error_log( 'Backing up: ' . $plugin_file );
350
351 $plugin_dir = WP_PLUGIN_DIR . '/' . dirname( $plugin_file );
352 $full_path = WP_PLUGIN_DIR . '/' . $plugin_file;
353
354 if ( ! file_exists( $full_path ) ) {
355 // error_log( 'Main file non trovato: ' . $full_path );
356 continue;
357 }
358
359 // Metadata & version.
360 $plugin_data = get_plugin_data( $full_path );
361 $version = $plugin_data['Version'];
362 // error_log( 'Version: ' . $version );
363
364 $slug = dirname( $plugin_file );
365 $zip_dirname = 'pr-' . $key . '-' . sanitize_file_name( $version ) . '-ver-' . $slug;
366 // error_log( 'zip_dirname: ' . $zip_dirname );
367
368 // Base dir & Backup destination.
369 $base_dir = plugin_dir_path( $full_path );
370 // error_log( 'base_dir: ' . $base_dir );
371 $backup_dir = trailingslashit( str_replace( $slug, $zip_dirname, $base_dir ) );
372 // error_log( 'backup_dir: ' . $backup_dir );
373
374 // Make Backup dir and move plugin files into it.
375 if ( ! $wp_filesystem->is_dir( $backup_dir ) ) {
376 $wp_filesystem->mkdir( $backup_dir );
377 }
378 if ( $wp_filesystem->is_dir( $backup_dir ) ) {
379 copy_dir( $plugin_dir, $backup_dir );
380
381 $zip_file = $zip_dirname . '.zip';
382 // error_log( 'zip_file: ' . $zip_file );
383
384 if ( eos_pv_create_zip_pclzip( $backup_dir, $zip_file ) ) {
385 $wp_filesystem->delete( untrailingslashit( $backup_dir ), true );
386 }
387
388 // Remove old versions of the plugin.
389 eos_plugin_revisions_remove_versions(
390 apply_filters( 'max_plugin_revisions', 4 ),
391 $slug
392 );
393 }
394 }
395
396 return $source;
397 }
398 add_filter( 'upgrader_source_selection', 'eos_plugin_unversal_backup', 10, 4 );