WP Rollback - Version 1.5

Version Description

  • New: You can now view plugin changelogs within the rollback screen. #7
  • New: Added support for WordPress Multisite rollbacks for themes and plugins. #22
  • New: Rollback button is fixed to the bottom of the page now to prevent long scrolls for rollbacks with many versions. #23
  • New: Updated the WP.org plugin header graphic. #37
Download this release

Release Info

Developer webdevmattcrom
Plugin Icon 128x128 WP Rollback
Version 1.5
Comparing to
See all releases

Code changes from version 1.4 to 1.5

assets/css/wp-rollback.css CHANGED
@@ -28,29 +28,23 @@
28
  font-weight: 600;
29
  }
30
 
31
- .rollback-form {
32
-
33
- }
34
-
35
  .wpr-versions-wrap {
36
  padding-left: 20px;
 
37
  }
38
 
39
- .rollback-form label {
40
- display: block;
41
- padding: 8px 0;
42
- font-size: 16px;
43
- }
44
-
45
- .rollback-form label input {
46
- margin-right: 5px;
47
- }
48
 
49
  .wpr-submit-wrap {
50
- margin: 25px 0 0;
51
  border-top: 1px solid #DADADA;
52
- padding: 20px 0 30px;
53
- max-width: 765px;
 
 
 
 
 
 
 
54
  }
55
 
56
  .rollback-form .current-version {
@@ -74,12 +68,24 @@
74
  margin-right: 5px !important;
75
  }
76
 
77
- /* Modal */
78
 
79
- #wpr-modal-confirm {
 
 
 
 
 
 
 
 
80
 
 
 
81
  }
82
 
 
 
83
  #wpr-modal-confirm .button-primary {
84
  margin-right: 5px;
85
  }
@@ -101,4 +107,45 @@
101
  .wpr-rollback-intro {
102
  font-size: 18px;
103
  margin-top: 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  }
28
  font-weight: 600;
29
  }
30
 
 
 
 
 
31
  .wpr-versions-wrap {
32
  padding-left: 20px;
33
+ margin-bottom:30px;
34
  }
35
 
 
 
 
 
 
 
 
 
 
36
 
37
  .wpr-submit-wrap {
 
38
  border-top: 1px solid #DADADA;
39
+ position: fixed;
40
+ width: 100%;
41
+ display: block;
42
+ bottom: 0;
43
+ background: #FFF;
44
+ margin: 0 0 0 -22px;
45
+ padding: 20px 0 20px 20px;
46
+ z-index: 9999;
47
+
48
  }
49
 
50
  .rollback-form .current-version {
68
  margin-right: 5px !important;
69
  }
70
 
71
+ /* List */
72
 
73
+ .wpr-version-li {
74
+ clear: left;
75
+ padding: 8px 0;
76
+ overflow: hidden;
77
+ }
78
+ .wpr-version-li label {
79
+ float:left;
80
+ font-size: 16px;
81
+ }
82
 
83
+ .wpr-version-li label input {
84
+ margin-right: 5px;
85
  }
86
 
87
+
88
+ /* Modal */
89
  #wpr-modal-confirm .button-primary {
90
  margin-right: 5px;
91
  }
107
  .wpr-rollback-intro {
108
  font-size: 18px;
109
  margin-top: 0;
110
+ }
111
+
112
+
113
+ /* Changelog link */
114
+ .wpr-changelog-link {
115
+ float: left;
116
+ margin: 4px 0 0 12px;
117
+ font-size: 11px;
118
+ font-style: italic;
119
+ opacity: 0;
120
+ line-height: 1;
121
+ transition: .15s ease-in-out;
122
+ -moz-transition: .15s ease-in-out;
123
+ -webkit-transition: .15s ease-in-out;
124
+ display: inline-block;
125
+ outline:none;
126
+ box-shadow:none !important;
127
+ text-decoration: none;
128
+ }
129
+ .wpr-changelog-link:hover {
130
+ text-decoration: underline;
131
+ }
132
+ .wpr-version-li:hover .wpr-changelog-link {
133
+ opacity: 0.75;
134
+ }
135
+ /* Hide full changelog */
136
+ .wpr-changelog, .wpr-hidden-changelog {
137
+ display: none;
138
+ }
139
+
140
+ /* The changelog that appears when loading via AJAX */
141
+ .wpr-changelog-entry {
142
+ padding: 6px 25px 18px;
143
+ background: #FFF;
144
+ margin: 20px 0;
145
+ max-height: 350px;
146
+ overflow-y: auto;
147
+ }
148
+
149
+ .wpr-no-changelog-message {
150
+ margin-bottom:0;
151
  }
assets/js/themes-wp-rollback.js CHANGED
@@ -7,172 +7,167 @@
7
  var wpr_vars;
8
  jQuery.noConflict();
9
 
10
- (function ($) {
11
-
12
- /**
13
- * Content Change DOM Event Listenter
14
- *
15
- * @see: http://stackoverflow.com/questions/3233991/jquery-watch-div/3234646#3234646
16
- * @param callback
17
- * @returns {*}
18
- */
19
- jQuery.fn.contentChange = function (callback) {
20
- var elms = jQuery(this);
21
- elms.each(
22
- function (i) {
23
- var elm = jQuery(this);
24
- elm.data("lastContents", elm.html());
25
- window.watchContentChange = window.watchContentChange ? window.watchContentChange : [];
26
- window.watchContentChange.push({"element": elm, "callback": callback});
27
- }
28
- );
29
- return elms;
30
- };
31
- setInterval(function () {
32
- if (window.watchContentChange) {
33
- for (i in window.watchContentChange) {
34
- if (window.watchContentChange[i].element.data("lastContents") != window.watchContentChange[i].element.html()) {
35
- window.watchContentChange[i].callback.apply(window.watchContentChange[i].element);
36
- window.watchContentChange[i].element.data("lastContents", window.watchContentChange[i].element.html())
37
- }
38
-
39
- }
40
- }
41
- }, 150);
42
-
43
- //On DOM Ready
44
- $(function () {
45
- var themes;
46
- themes = wp.themes = wp.themes || {};
47
- themes.data = _wpThemeSettings;
48
-
49
- //On clicking a theme template
50
- $('.theme-overlay').contentChange(function (e) {
51
-
52
- //get theme name that was clicked
53
- var clicked_theme = wpr_get_parameter_by_name('theme');
54
-
55
- //check that rollback button hasn't been placed
56
- if (is_rollback_btn_there()) {
57
- //button is there, bail
58
- return false;
59
- }
60
-
61
- //pass off to rollback function
62
- wpr_theme_rollback(clicked_theme);
63
-
64
-
65
- });
66
-
67
-
68
- /**
69
- * Check to see if Rollback button is in place
70
- *
71
- * @returns {boolean}
72
- */
73
- function is_rollback_btn_there() {
74
-
75
- if ($('.wpr-theme-rollback').length > 0) {
76
- return true;
77
- }
78
- return false;
79
-
80
-
81
- }
82
-
83
- /**
84
- * Is Theme WordPress.org?
85
- *
86
- * @description Rollback only supports WordPress.org themes
87
- */
88
- function wpr_theme_rollback(theme) {
89
-
90
- var theme_data = wpr_get_theme_data(theme);
91
-
92
- //ensure this theme can be rolled back (not premium, etc)
93
- if (theme_data !== null && typeof theme_data.hasRollback !== 'undefined' && theme_data.hasRollback !== false) {
94
-
95
- var active_theme = $('.theme-overlay').hasClass('active');
96
-
97
-
98
- var rollback_btn_html = '<a href="' + encodeURI('index.php?page=wp-rollback&type=theme&theme_file=' + theme + '&current_version=' + theme_data.version + '&rollback_name=' + theme_data.name + '&_wpnonce=' + wpr_vars.nonce) + '" style="position:absolute;right: ' + (active_theme === true ? '5px' : '80px') + '; bottom: 5px;" class="button wpr-theme-rollback">' + wpr_vars.text_rollback_label + '</a>';
99
-
100
- $('.theme-wrap').find('.theme-actions').append(rollback_btn_html);
101
-
102
- } else {
103
- //Can't roll back this theme, display the notice.
104
- $('.theme-wrap').find('.theme-actions').append('<span class="no-rollback" style="position: absolute;left: 23px;bottom: 16px;font-size: 12px;font-style: italic;color: rgb(181, 181, 181);">' + wpr_vars.text_not_rollbackable + '</span>');
105
- }
106
-
107
- }
108
-
109
- /**
110
- * Get Theme Data
111
- *
112
- * @description Loops through the wp.themes.data.themes object, finds a match, and returns the data
113
- * @param theme
114
- * @returns {*}
115
- */
116
- function wpr_get_theme_data(theme) {
117
- var theme_data = wp.themes.data.themes;
118
-
119
- //Loop through complete theme data to find this current theme's data
120
- for (var i = 0, len = theme_data.length; i < len; i++) {
121
- if (theme_data[i].id === theme) {
122
- return theme_data[i]; // Return as soon as the object is found
123
- }
124
- }
125
- return null; // The object was not found
126
- }
127
-
128
- /**
129
- * JS Ready Query String (Helper)
130
- *
131
- * Kinda dirty but whatever...
132
- *
133
- * @see: http://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript
134
- * @param name
135
- * @returns {string}
136
- */
137
- function wpr_get_parameter_by_name(name) {
138
- name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
139
- var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
140
- results = regex.exec(location.search);
141
- return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
142
- }
143
-
144
- /**
145
- * Get Parameter from Focused Theme
146
- *
147
- * @returns {*}
148
- */
149
- function wpr_get_parameter_from_focused_theme() {
150
- var focused_theme = wp.themes.focusedTheme;
151
- var name = $(focused_theme).find('.theme-name').attr('id');
152
-
153
- if (typeof name !== 'undefined') {
154
- name = name.replace('-name', '');
155
- } else {
156
- return false;
157
- }
158
-
159
- return name;
160
- }
161
-
162
-
163
- /**
164
- * Theme Rollback Button Clicked
165
- *
166
- * @description Send them over to
167
- */
168
- $('body').on('click', '.wpr-theme-rollback', function (e) {
169
-
170
- window.location = $(this).attr('href');
171
-
172
- });
173
-
174
-
175
- });
176
-
177
-
178
- })(jQuery);
7
  var wpr_vars;
8
  jQuery.noConflict();
9
 
10
+ (function( $ ) {
11
+
12
+ /**
13
+ * Content Change DOM Event Listenter
14
+ *
15
+ * @see: http://stackoverflow.com/questions/3233991/jquery-watch-div/3234646#3234646
16
+ * @param callback
17
+ * @returns {*}
18
+ */
19
+ jQuery.fn.contentChange = function( callback ) {
20
+ var elms = jQuery( this );
21
+ elms.each(
22
+ function( i ) {
23
+ var elm = jQuery( this );
24
+ elm.data( 'lastContents', elm.html() );
25
+ window.watchContentChange = window.watchContentChange ? window.watchContentChange : [];
26
+ window.watchContentChange.push( { 'element': elm, 'callback': callback } );
27
+ }
28
+ );
29
+ return elms;
30
+ };
31
+ setInterval( function() {
32
+ if ( window.watchContentChange ) {
33
+ for ( i in window.watchContentChange ) {
34
+ if ( window.watchContentChange[ i ].element.data( 'lastContents' ) != window.watchContentChange[ i ].element.html() ) {
35
+ window.watchContentChange[ i ].callback.apply( window.watchContentChange[ i ].element );
36
+ window.watchContentChange[ i ].element.data( 'lastContents', window.watchContentChange[ i ].element.html() );
37
+ }
38
+
39
+ }
40
+ }
41
+ }, 150 );
42
+
43
+ // On DOM Ready
44
+ $( function() {
45
+ var themes;
46
+
47
+ themes = wp.themes = wp.themes || {};
48
+ themes.data = typeof _wpThemeSettings !== 'undefined' ? _wpThemeSettings : '';
49
+
50
+ // On clicking a theme template
51
+ $( '.theme-overlay' ).contentChange( function( e ) {
52
+
53
+ // get theme name that was clicked
54
+ var clicked_theme = wpr_get_parameter_by_name( 'theme' );
55
+
56
+ // check that rollback button hasn't been placed
57
+ if ( is_rollback_btn_there() ) {
58
+ // button is there, bail
59
+ return false;
60
+ }
61
+
62
+ // pass off to rollback function
63
+ wpr_theme_rollback( clicked_theme );
64
+
65
+ } );
66
+
67
+ /**
68
+ * Check to see if Rollback button is in place
69
+ *
70
+ * @returns {boolean}
71
+ */
72
+ function is_rollback_btn_there() {
73
+
74
+ if ( $( '.wpr-theme-rollback' ).length > 0 ) {
75
+ return true;
76
+ }
77
+ return false;
78
+
79
+ }
80
+
81
+ /**
82
+ * Is Theme WordPress.org?
83
+ *
84
+ * @description Rollback only supports WordPress.org themes
85
+ */
86
+ function wpr_theme_rollback( theme ) {
87
+
88
+ var theme_data = wpr_get_theme_data( theme );
89
+
90
+ // ensure this theme can be rolled back (not premium, etc)
91
+ if ( theme_data !== null && typeof theme_data.hasRollback !== 'undefined' && theme_data.hasRollback !== false ) {
92
+
93
+ var active_theme = $( '.theme-overlay' ).hasClass( 'active' );
94
+
95
+ var rollback_btn_html = '<a href="' + encodeURI( 'index.php?page=wp-rollback&type=theme&theme_file=' + theme + '&current_version=' + theme_data.version + '&rollback_name=' + theme_data.name + '&_wpnonce=' + wpr_vars.nonce ) + '" style="position:absolute;right: ' + (active_theme === true ? '5px' : '80px') + '; bottom: 5px;" class="button wpr-theme-rollback">' + wpr_vars.text_rollback_label + '</a>';
96
+
97
+ $( '.theme-wrap' ).find( '.theme-actions' ).append( rollback_btn_html );
98
+
99
+ } else {
100
+ // Can't roll back this theme, display the notice.
101
+ $( '.theme-wrap' ).find( '.theme-actions' ).append( '<span class="no-rollback" style="position: absolute;left: 23px;bottom: 16px;font-size: 12px;font-style: italic;color: rgb(181, 181, 181);">' + wpr_vars.text_not_rollbackable + '</span>' );
102
+ }
103
+
104
+ }
105
+
106
+ /**
107
+ * Get Theme Data
108
+ *
109
+ * @description Loops through the wp.themes.data.themes object, finds a match, and returns the data
110
+ * @param theme
111
+ * @returns {*}
112
+ */
113
+ function wpr_get_theme_data( theme ) {
114
+
115
+ var theme_data = wp.themes.data.themes;
116
+
117
+ // Loop through complete theme data to find this current theme's data
118
+ for ( var i = 0, len = theme_data.length; i < len; i ++ ) {
119
+ if ( theme_data[ i ].id === theme ) {
120
+ return theme_data[ i ]; // Return as soon as the object is found
121
+ }
122
+ }
123
+ return null; // The object was not found
124
+ }
125
+
126
+ /**
127
+ * JS Ready Query String (Helper)
128
+ *
129
+ * Kinda dirty but whatever...
130
+ *
131
+ * @see: http://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript
132
+ * @param name
133
+ * @returns {string}
134
+ */
135
+ function wpr_get_parameter_by_name( name ) {
136
+ name = name.replace( /[\[]/, '\\[' ).replace( /[\]]/, '\\]' );
137
+ var regex = new RegExp( '[\\?&]' + name + '=([^&#]*)' ),
138
+ results = regex.exec( location.search );
139
+ return results === null ? '' : decodeURIComponent( results[ 1 ].replace( /\+/g, ' ' ) );
140
+ }
141
+
142
+ /**
143
+ * Get Parameter from Focused Theme
144
+ *
145
+ * @returns {*}
146
+ */
147
+ function wpr_get_parameter_from_focused_theme() {
148
+ var focused_theme = wp.themes.focusedTheme;
149
+ var name = $( focused_theme ).find( '.theme-name' ).attr( 'id' );
150
+
151
+ if ( typeof name !== 'undefined' ) {
152
+ name = name.replace( '-name', '' );
153
+ } else {
154
+ return false;
155
+ }
156
+
157
+ return name;
158
+ }
159
+
160
+ /**
161
+ * Theme Rollback Button Clicked
162
+ *
163
+ * Send them over to rollback.
164
+ */
165
+ $( 'body' ).on( 'click', '.wpr-theme-rollback', function( e ) {
166
+
167
+ window.location = $( this ).attr( 'href' );
168
+
169
+ } );
170
+
171
+ } );
172
+
173
+ })( jQuery );
 
 
 
 
 
assets/js/wp-rollback.js CHANGED
@@ -8,58 +8,152 @@
8
  var wpr_vars;
9
 
10
  jQuery.noConflict();
11
- (function ( $ ) {
12
 
13
- //On DOM Ready
14
- $( function () {
15
 
16
  var form = $( '.rollback-form' );
17
  var form_labels = $( 'label', form.get( 0 ) );
18
  var form_submit_btn = $( '.magnific-popup' );
19
 
20
- //On Element Click
21
- form_labels.on( 'click', function () {
 
 
22
 
23
- //add a selected class
24
  form_labels.removeClass( 'wpr-selected' );
25
  form_submit_btn.removeClass( 'wpr-rollback-disabled' );
26
  $( this ).addClass( 'wpr-selected' );
27
 
28
- //ensure the radio button always gets clicked
29
  $( this ).find( 'input' ).prop( 'checked', true );
30
 
31
  } );
32
 
33
- //Modal
34
- form_submit_btn.on( 'click', function () {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
 
36
  var rollback_form_vals = form.serializeArray();
37
 
38
  var rollback_version = form.find( 'input[name="plugin_version"]:checked' ).val();
39
- if ( !rollback_version ) {
40
  rollback_version = form.find( 'input[name="theme_version"]:checked' ).val();
41
  }
42
  var installed_version = form.find( 'input[name="installed_version"]' ).val();
43
  var new_version = form.find( 'input[name="new_version"]' ).val();
44
  var rollback_name = form.find( 'input[name="rollback_name"]' ).val();
45
 
46
- //Ensure a version is selected
47
- if ( !rollback_version ) {
48
 
49
  alert( wpr_vars.version_missing );
50
- $.magnificPopup.close(); //just for good measure
51
 
52
  } else {
53
 
54
- //Passed
55
  $.magnificPopup.open( {
56
- items : {
57
- src : $( '#wpr-modal-confirm' ), // can be a HTML string, jQuery object, or CSS selector
58
  type: 'inline'
59
  },
60
  closeBtnInside: false,
61
- callbacks : {
62
- open: function () {
63
 
64
  $( 'span.wpr-plugin-name' ).text( rollback_name );
65
  $( 'span.wpr-installed-version' ).text( installed_version );
@@ -69,23 +163,21 @@ jQuery.noConflict();
69
  }
70
  } );
71
 
72
-
73
  }
74
 
75
  } );
76
 
77
- //Modal Close
78
- $( '.wpr-close' ).on( 'click', function ( e ) {
79
  e.preventDefault();
80
  $.magnificPopup.close();
81
  } );
82
- //Modal Confirm (GO! GO! GO!)
83
- $( '.wpr-go' ).on( 'click', function ( e ) {
84
- //submit form
85
  form.submit();
86
  } );
87
 
88
  } );
89
 
90
-
91
- })( jQuery );
8
  var wpr_vars;
9
 
10
  jQuery.noConflict();
11
+ (function( $ ) {
12
 
13
+ // On DOM Ready
14
+ $( function() {
15
 
16
  var form = $( '.rollback-form' );
17
  var form_labels = $( 'label', form.get( 0 ) );
18
  var form_submit_btn = $( '.magnific-popup' );
19
 
20
+ /**
21
+ * On version click
22
+ */
23
+ form_labels.on( 'click', function() {
24
 
25
+ // add a selected class
26
  form_labels.removeClass( 'wpr-selected' );
27
  form_submit_btn.removeClass( 'wpr-rollback-disabled' );
28
  $( this ).addClass( 'wpr-selected' );
29
 
30
+ // ensure the radio button always gets clicked
31
  $( this ).find( 'input' ).prop( 'checked', true );
32
 
33
  } );
34
 
35
+ /**
36
+ * On view changelog clicked.
37
+ */
38
+ $( '.wpr-changelog-link' ).on( 'click', function( e ) {
39
+
40
+ e.preventDefault();
41
+
42
+ var changelog_container = $( '.wpr-changelog' );
43
+ var changelog_placement = $( this ).parent( 'li' );
44
+ var version = $( this ).data( 'version' );
45
+
46
+ // Ensure all change log links are visible.
47
+ $('.wpr-changelog-link').removeClass('wpr-hidden-changelog')
48
+
49
+ // If changelog was already fetched, use that data.
50
+ if ( changelog_container.html().length ) {
51
+ wpr_append_changelog_entry( changelog_placement, version );
52
+ return false;
53
+ }
54
+
55
+ // Get changelog via AJAX.
56
+ $.post( ajaxurl, {
57
+ 'action': 'wpr_check_changelog',
58
+ 'slug': $( 'input[name="plugin_slug"]' ).val()
59
+ }, function( response ) {
60
+ // Add changelog to DOM.
61
+ $( changelog_container ).append( $.parseHTML( response ) );
62
+
63
+ // Show changelog entry.
64
+ wpr_append_changelog_entry( changelog_placement, version );
65
+
66
+ }
67
+ );
68
+
69
+ } );
70
+
71
+ /**
72
+ * Show changelog entry.
73
+ *
74
+ * @param placement
75
+ * @param version
76
+ */
77
+ function wpr_append_changelog_entry( placement, version ) {
78
+
79
+ var changelog = $( '.wpr-changelog' );
80
+ var changelog_headings = $( changelog ).find( 'h4' );
81
+
82
+ // Remove old entry.
83
+ $( '.wpr-changelog-entry' ).remove();
84
+
85
+ // Hide this change log link.
86
+ $(placement).find('.wpr-changelog-link').addClass('wpr-hidden-changelog');
87
+
88
+ // Append a new one.
89
+ $( placement ).after( '<div class="wpr-changelog-entry"></div>' );
90
+
91
+ // Loop through changelog headings to get changelog entry.
92
+ $( changelog_headings ).each( function( index, value ) {
93
+
94
+ var raw_val = $( value ).text();
95
+
96
+ // Match the changelog version heading using regex from: https://github.com/sindresorhus/semver-regex/blob/master/index.js
97
+ // var regex_symver = /\bv?(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-[\da-z\-]+(?:\.[\da-z\-]+)*)?(?:\+[\da-z\-]+(?:\.[\da-z\-]+)*)?\b/;
98
+
99
+ // from: https://stackoverflow.com/a/27540795/684352
100
+ var regex_symver = /(?:(\d+)\.)?(?:(\d+)\.)?(?:(\d+)\.\d+)?(?:(\d+)\.\d+)/;
101
+ var found_version_num = raw_val.match( regex_symver );
102
+ var found_version_num = $( found_version_num ).get( 0 );
103
+
104
+ // Match version number.
105
+ if ( found_version_num == version ) {
106
+
107
+ // Assemble entry.
108
+ var changelog_heading = $( value ).clone();
109
+ var changelog_entry = $( value ).nextUntil( 'h4' ).clone();
110
+
111
+ // Append changelog entry.
112
+ $( '.wpr-changelog-entry' ).append( changelog_heading ).append( changelog_entry );
113
+
114
+ }
115
+
116
+ } );
117
+
118
+ // If no changelog found, show message.
119
+ if ( ! $( '.wpr-changelog-entry' ).html().length ) {
120
+ $( '.wpr-changelog-entry' ).append( '<p class="wpr-no-changelog-message">' + wpr_vars.text_no_changelog_found + '</p>' );
121
+ }
122
+
123
+ }
124
+
125
+ /**
126
+ * Modal rollback.
127
+ */
128
+ form_submit_btn.on( 'click', function() {
129
 
130
  var rollback_form_vals = form.serializeArray();
131
 
132
  var rollback_version = form.find( 'input[name="plugin_version"]:checked' ).val();
133
+ if ( ! rollback_version ) {
134
  rollback_version = form.find( 'input[name="theme_version"]:checked' ).val();
135
  }
136
  var installed_version = form.find( 'input[name="installed_version"]' ).val();
137
  var new_version = form.find( 'input[name="new_version"]' ).val();
138
  var rollback_name = form.find( 'input[name="rollback_name"]' ).val();
139
 
140
+ // Ensure a version is selected
141
+ if ( ! rollback_version ) {
142
 
143
  alert( wpr_vars.version_missing );
144
+ $.magnificPopup.close(); // just for good measure
145
 
146
  } else {
147
 
148
+ // Passed
149
  $.magnificPopup.open( {
150
+ items: {
151
+ src: $( '#wpr-modal-confirm' ), // can be a HTML string, jQuery object, or CSS selector
152
  type: 'inline'
153
  },
154
  closeBtnInside: false,
155
+ callbacks: {
156
+ open: function() {
157
 
158
  $( 'span.wpr-plugin-name' ).text( rollback_name );
159
  $( 'span.wpr-installed-version' ).text( installed_version );
163
  }
164
  } );
165
 
 
166
  }
167
 
168
  } );
169
 
170
+ // Modal Close
171
+ $( '.wpr-close' ).on( 'click', function( e ) {
172
  e.preventDefault();
173
  $.magnificPopup.close();
174
  } );
175
+ // Modal Confirm (GO! GO! GO!)
176
+ $( '.wpr-go' ).on( 'click', function( e ) {
177
+ // submit form
178
  form.submit();
179
  } );
180
 
181
  } );
182
 
183
+ })( jQuery );
 
includes/class-rollback-plugin-upgrader.php CHANGED
@@ -1,20 +1,31 @@
1
  <?php
2
  /**
3
- * WP Rollback Plugin Upgrader
 
 
4
  *
5
- * @description: Class that extends the WP Core Plugin_Upgrader found in core to do rollbacks
6
  * @copyright : http://opensource.org/licenses/gpl-2.0.php GNU Public License
7
  * @since : 1.0.0
8
  */
9
 
10
- // Exit if accessed directly
11
  if ( ! defined( 'ABSPATH' ) ) {
12
  exit;
13
  }
14
 
15
- //kick things off
 
 
16
  class WP_Rollback_Plugin_Upgrader extends Plugin_Upgrader {
17
 
 
 
 
 
 
 
 
 
18
  public function rollback( $plugin, $args = array() ) {
19
 
20
  $defaults = array(
@@ -66,10 +77,10 @@ class WP_Rollback_Plugin_Upgrader extends Plugin_Upgrader {
66
  return $this->result;
67
  }
68
 
69
- // Force refresh of plugin update information
70
  wp_clean_plugins_cache( $parsed_args['clear_update_cache'] );
71
 
72
  return true;
73
  }
74
 
75
- }
1
  <?php
2
  /**
3
+ * WP Rollback Plugin Upgrader
4
+ *
5
+ * Class that extends the WP Core Plugin_Upgrader found in core to do rollbacks.
6
  *
 
7
  * @copyright : http://opensource.org/licenses/gpl-2.0.php GNU Public License
8
  * @since : 1.0.0
9
  */
10
 
11
+ // Exit if accessed directly.
12
  if ( ! defined( 'ABSPATH' ) ) {
13
  exit;
14
  }
15
 
16
+ /**
17
+ * Class WP_Rollback_Plugin_Upgrader
18
+ */
19
  class WP_Rollback_Plugin_Upgrader extends Plugin_Upgrader {
20
 
21
+ /**
22
+ * Plugin rollback.
23
+ *
24
+ * @param $plugin
25
+ * @param array $args
26
+ *
27
+ * @return array|bool|\WP_Error
28
+ */
29
  public function rollback( $plugin, $args = array() ) {
30
 
31
  $defaults = array(
77
  return $this->result;
78
  }
79
 
80
+ // Force refresh of plugin update information.
81
  wp_clean_plugins_cache( $parsed_args['clear_update_cache'] );
82
 
83
  return true;
84
  }
85
 
86
+ }
includes/class-rollback-theme-upgrader.php CHANGED
@@ -1,20 +1,31 @@
1
  <?php
2
  /**
3
- * WP Rollback Theme Upgrader
 
 
4
  *
5
- * @description: Class that extends the WP Core Theme_Upgrader found in core to do rollbacks
6
  * @copyright : http://opensource.org/licenses/gpl-2.0.php GNU Public License
7
  * @since : 1.0.0
8
  */
9
 
10
- // Exit if accessed directly
11
  if ( ! defined( 'ABSPATH' ) ) {
12
  exit;
13
  }
14
 
15
- //kick things off
 
 
16
  class WP_Rollback_Theme_Upgrader extends Theme_Upgrader {
17
 
 
 
 
 
 
 
 
 
18
  public function rollback( $theme, $args = array() ) {
19
 
20
  $defaults = array(
@@ -42,13 +53,11 @@ class WP_Rollback_Theme_Upgrader extends Theme_Upgrader {
42
 
43
  $url = $download_endpoint . $theme_slug . '.' . $theme_version . '.zip';
44
 
45
-
46
  add_filter( 'upgrader_pre_install', array( $this, 'current_before' ), 10, 2 );
47
  add_filter( 'upgrader_post_install', array( $this, 'current_after' ), 10, 2 );
48
  add_filter( 'upgrader_clear_destination', array( $this, 'delete_old_theme' ), 10, 4 );
49
 
50
- //'source_selection' => array($this, 'source_selection'), //there's a trac ticket to move up the directory for zip's which are made a bit differently, useful for non-.org plugins.
51
-
52
  $this->run( array(
53
  'package' => $url,
54
  'destination' => get_theme_root(),
@@ -61,7 +70,6 @@ class WP_Rollback_Theme_Upgrader extends Theme_Upgrader {
61
  ),
62
  ) );
63
 
64
-
65
  remove_filter( 'upgrader_pre_install', array( $this, 'current_before' ) );
66
  remove_filter( 'upgrader_post_install', array( $this, 'current_after' ) );
67
  remove_filter( 'upgrader_clear_destination', array( $this, 'delete_old_theme' ) );
@@ -70,11 +78,11 @@ class WP_Rollback_Theme_Upgrader extends Theme_Upgrader {
70
  return $this->result;
71
  }
72
 
73
- // Force refresh of theme update information
74
  wp_clean_themes_cache( $parsed_args['clear_update_cache'] );
75
 
76
  return true;
77
 
78
  }
79
 
80
- }
1
  <?php
2
  /**
3
+ * WP Rollback Theme Upgrader
4
+ *
5
+ * Class that extends the WP Core Theme_Upgrader found in core to do rollbacks.
6
  *
 
7
  * @copyright : http://opensource.org/licenses/gpl-2.0.php GNU Public License
8
  * @since : 1.0.0
9
  */
10
 
11
+ // Exit if accessed directly.
12
  if ( ! defined( 'ABSPATH' ) ) {
13
  exit;
14
  }
15
 
16
+ /**
17
+ * Class WP_Rollback_Theme_Upgrader
18
+ */
19
  class WP_Rollback_Theme_Upgrader extends Theme_Upgrader {
20
 
21
+ /**
22
+ * Theme rollback.
23
+ *
24
+ * @param $theme
25
+ * @param array $args
26
+ *
27
+ * @return array|bool|\WP_Error
28
+ */
29
  public function rollback( $theme, $args = array() ) {
30
 
31
  $defaults = array(
53
 
54
  $url = $download_endpoint . $theme_slug . '.' . $theme_version . '.zip';
55
 
 
56
  add_filter( 'upgrader_pre_install', array( $this, 'current_before' ), 10, 2 );
57
  add_filter( 'upgrader_post_install', array( $this, 'current_after' ), 10, 2 );
58
  add_filter( 'upgrader_clear_destination', array( $this, 'delete_old_theme' ), 10, 4 );
59
 
60
+ // 'source_selection' => array($this, 'source_selection'), //there's a trac ticket to move up the directory for zip's which are made a bit differently, useful for non-.org plugins.
 
61
  $this->run( array(
62
  'package' => $url,
63
  'destination' => get_theme_root(),
70
  ),
71
  ) );
72
 
 
73
  remove_filter( 'upgrader_pre_install', array( $this, 'current_before' ) );
74
  remove_filter( 'upgrader_post_install', array( $this, 'current_after' ) );
75
  remove_filter( 'upgrader_clear_destination', array( $this, 'delete_old_theme' ) );
78
  return $this->result;
79
  }
80
 
81
+ // Force refresh of theme update information.
82
  wp_clean_themes_cache( $parsed_args['clear_update_cache'] );
83
 
84
  return true;
85
 
86
  }
87
 
88
+ }
includes/rollback-action.php CHANGED
@@ -3,7 +3,7 @@
3
  * Rollback Action.
4
  */
5
 
6
- // Exit if accessed directly
7
  if ( ! defined( 'ABSPATH' ) ) {
8
  exit;
9
  }
@@ -13,10 +13,10 @@ $url = 'index.php?page=wp-rollback&plugin_file=' . esc_url( $args['plugin_fi
13
  $plugin = $this->plugin_slug;
14
  $version = $args['plugin_version'];
15
 
16
- //Theme rollback
17
  if ( ! empty( $_GET['theme_file'] ) && file_exists( WP_CONTENT_DIR . '/themes/' . $_GET['theme_file'] ) ) {
18
 
19
- //theme specific vars
20
  $nonce = 'upgrade-theme_' . $_GET['theme_file'];
21
  $url = 'index.php?page=wp-rollback&theme_file=' . $args['theme_file'] . 'action=upgrade-theme';
22
  $version = $_GET['theme_version'];
@@ -27,11 +27,11 @@ if ( ! empty( $_GET['theme_file'] ) && file_exists( WP_CONTENT_DIR . '/themes/'
27
  $upgrader->rollback( $_GET['theme_file'] );
28
 
29
  } elseif ( ! empty( $_GET['plugin_file'] ) && file_exists( WP_PLUGIN_DIR . '/' . $_GET['plugin_file'] ) ) {
30
- //This is a plugin rollback
31
  $upgrader = new WP_Rollback_Plugin_Upgrader( new Plugin_Upgrader_Skin( compact( 'title', 'nonce', 'url', 'plugin', 'version' ) ) );
32
 
33
  $upgrader->rollback( $this->plugin_file );
34
  } else {
35
- _e( 'This rollback request is a missing proper query string. Please contact support.', 'wp-rollback' );
36
  }
37
 
3
  * Rollback Action.
4
  */
5
 
6
+ // Exit if accessed directly.
7
  if ( ! defined( 'ABSPATH' ) ) {
8
  exit;
9
  }
13
  $plugin = $this->plugin_slug;
14
  $version = $args['plugin_version'];
15
 
16
+ // Theme rollback.
17
  if ( ! empty( $_GET['theme_file'] ) && file_exists( WP_CONTENT_DIR . '/themes/' . $_GET['theme_file'] ) ) {
18
 
19
+ // Theme specific vars.
20
  $nonce = 'upgrade-theme_' . $_GET['theme_file'];
21
  $url = 'index.php?page=wp-rollback&theme_file=' . $args['theme_file'] . 'action=upgrade-theme';
22
  $version = $_GET['theme_version'];
27
  $upgrader->rollback( $_GET['theme_file'] );
28
 
29
  } elseif ( ! empty( $_GET['plugin_file'] ) && file_exists( WP_PLUGIN_DIR . '/' . $_GET['plugin_file'] ) ) {
30
+ // This is a plugin rollback.
31
  $upgrader = new WP_Rollback_Plugin_Upgrader( new Plugin_Upgrader_Skin( compact( 'title', 'nonce', 'url', 'plugin', 'version' ) ) );
32
 
33
  $upgrader->rollback( $this->plugin_file );
34
  } else {
35
+ _e( 'This rollback request is missing a proper query string. Please contact support.', 'wp-rollback' );
36
  }
37
 
includes/rollback-menu.php CHANGED
@@ -2,12 +2,10 @@
2
  /**
3
  * Rollback Menu
4
  *
5
- * @description Provides the rollback screen view with releases and
6
- *
7
  */
8
 
9
-
10
- //Ensure we have our necessary query strings
11
  if ( ( ! isset( $_GET['type'] ) && ! isset( $_GET['theme'] ) ) || ( ! isset( $_GET['type'] ) && ! isset( $_GET['plugin_file'] ) ) ) {
12
  wp_die( __( 'WP Rollback is missing necessary parameters to continue. Please contact support.', 'wp-rollback' ) );
13
  }
@@ -21,31 +19,32 @@ $plugins = get_plugins();
21
  <div class="wpr-content-wrap">
22
 
23
  <h1>
24
- <img src="<?php echo WP_ROLLBACK_PLUGIN_URL; ?>/assets/images/wprb-icon-final.svg" onerror="this.onerror=null; this.src='<?php echo WP_ROLLBACK_PLUGIN_URL; ?>/assets/images/wprb-logo.png'"><?php _e( 'WP Rollback', 'wp-rollback' ); ?>
 
25
  </h1>
26
 
27
  <p><?php echo apply_filters( 'wpr_rollback_description', sprintf( __( 'Please select which %1$s version you would like to rollback to from the releases listed below. You currently have version %2$s installed of %3$s.', 'wp-rollback' ), '<span class="type">' . ( $theme_rollback == true ? __( 'theme', 'wp-rollback' ) : __( 'plugin', 'wp-rollback' ) ) . '</span>', '<span class="current-version">' . esc_html( $args['current_version'] ) . '</span>', '<span class="rollback-name">' . esc_html( $args['rollback_name'] ) . '</span>' ) ); ?></p>
28
 
 
29
  </div>
30
 
31
  <?php if ( isset( $args['plugin_file'] ) && in_array( $args['plugin_file'], array_keys( $plugins ) ) ) {
32
  $versions = WP_Rollback()->versions_select( 'plugin' );
33
  } elseif ( $theme_rollback == true && isset( $_GET['theme_file'] ) ) {
34
- //theme rollback: set up our theme vars
35
  $svn_tags = WP_Rollback()->get_svn_tags( 'theme', $_GET['theme_file'] );
36
- $this->set_svn_versions_data( $svn_tags );
37
  $this->current_version = $_GET['current_version'];
38
  $versions = WP_Rollback()->versions_select( 'theme' );
39
 
40
  } else {
41
- //Fallback check
42
- wp_die( 'Oh no! We\'re missing required rollback query strings. Please contact support so we can check this bug out and squash it!', 'wp-rollback' );
43
- }
44
- ?>
45
 
46
  <form name="check_for_rollbacks" class="rollback-form" action="<?php echo admin_url( '/index.php' ); ?>">
47
  <?php
48
- //Output Versions
49
  if ( ! empty( $versions ) ) { ?>
50
 
51
  <div class="wpr-versions-wrap">
@@ -68,9 +67,10 @@ $plugins = get_plugins();
68
  <?php do_action( 'wpr_hidden_fields' ); ?>
69
  <input type="hidden" name="page" value="wp-rollback">
70
  <?php
71
- //Important: We need the appropriate file to perform a rollback
72
  if ( $plugin_rollback == true ) { ?>
73
  <input type="hidden" name="plugin_file" value="<?php echo esc_attr( $args['plugin_file'] ); ?>">
 
74
  <?php } else { ?>
75
  <input type="hidden" name="theme_file" value="<?php echo esc_attr( $_GET['theme_file'] ); ?>">
76
  <?php } ?>
@@ -80,9 +80,7 @@ $plugins = get_plugins();
80
 
81
  <div id="wpr-modal-confirm" class="white-popup mfp-hide">
82
  <div class="wpr-modal-inner">
83
- <p class="wpr-rollback-intro"><?php
84
- _e( 'Are you sure you want to perform the following rollback?', 'wp-rollback' );
85
- ?></p>
86
 
87
  <div class="rollback-details">
88
  <table class="widefat">
@@ -128,4 +126,4 @@ $plugins = get_plugins();
128
 
129
  </form>
130
 
131
- </div>
2
  /**
3
  * Rollback Menu
4
  *
5
+ * Provides the rollback screen view with releases.
 
6
  */
7
 
8
+ // Ensure we have our necessary query strings
 
9
  if ( ( ! isset( $_GET['type'] ) && ! isset( $_GET['theme'] ) ) || ( ! isset( $_GET['type'] ) && ! isset( $_GET['plugin_file'] ) ) ) {
10
  wp_die( __( 'WP Rollback is missing necessary parameters to continue. Please contact support.', 'wp-rollback' ) );
11
  }
19
  <div class="wpr-content-wrap">
20
 
21
  <h1>
22
+ <img src="<?php echo WP_ROLLBACK_PLUGIN_URL; ?>/assets/images/wprb-icon-final.svg"
23
+ onerror="this.onerror=null; this.src='<?php echo WP_ROLLBACK_PLUGIN_URL; ?>/assets/images/wprb-logo.png';"><?php _e( 'WP Rollback', 'wp-rollback' ); ?>
24
  </h1>
25
 
26
  <p><?php echo apply_filters( 'wpr_rollback_description', sprintf( __( 'Please select which %1$s version you would like to rollback to from the releases listed below. You currently have version %2$s installed of %3$s.', 'wp-rollback' ), '<span class="type">' . ( $theme_rollback == true ? __( 'theme', 'wp-rollback' ) : __( 'plugin', 'wp-rollback' ) ) . '</span>', '<span class="current-version">' . esc_html( $args['current_version'] ) . '</span>', '<span class="rollback-name">' . esc_html( $args['rollback_name'] ) . '</span>' ) ); ?></p>
27
 
28
+ <div class="wpr-changelog"></div>
29
  </div>
30
 
31
  <?php if ( isset( $args['plugin_file'] ) && in_array( $args['plugin_file'], array_keys( $plugins ) ) ) {
32
  $versions = WP_Rollback()->versions_select( 'plugin' );
33
  } elseif ( $theme_rollback == true && isset( $_GET['theme_file'] ) ) {
34
+ // Theme rollback: set up our theme vars
35
  $svn_tags = WP_Rollback()->get_svn_tags( 'theme', $_GET['theme_file'] );
36
+ WP_Rollback()->set_svn_versions_data( $svn_tags );
37
  $this->current_version = $_GET['current_version'];
38
  $versions = WP_Rollback()->versions_select( 'theme' );
39
 
40
  } else {
41
+ // Fallback check
42
+ wp_die( __( 'Oh no! We\'re missing required rollback query strings. Please contact support so we can check this bug out and squash it!', 'wp-rollback' ) );
43
+ } ?>
 
44
 
45
  <form name="check_for_rollbacks" class="rollback-form" action="<?php echo admin_url( '/index.php' ); ?>">
46
  <?php
47
+ // Output Versions
48
  if ( ! empty( $versions ) ) { ?>
49
 
50
  <div class="wpr-versions-wrap">
67
  <?php do_action( 'wpr_hidden_fields' ); ?>
68
  <input type="hidden" name="page" value="wp-rollback">
69
  <?php
70
+ // Important: We need the appropriate file to perform a rollback
71
  if ( $plugin_rollback == true ) { ?>
72
  <input type="hidden" name="plugin_file" value="<?php echo esc_attr( $args['plugin_file'] ); ?>">
73
+ <input type="hidden" name="plugin_slug" value="<?php echo esc_attr( $args['plugin_slug'] ); ?>">
74
  <?php } else { ?>
75
  <input type="hidden" name="theme_file" value="<?php echo esc_attr( $_GET['theme_file'] ); ?>">
76
  <?php } ?>
80
 
81
  <div id="wpr-modal-confirm" class="white-popup mfp-hide">
82
  <div class="wpr-modal-inner">
83
+ <p class="wpr-rollback-intro"><?php _e( 'Are you sure you want to perform the following rollback?', 'wp-rollback' ); ?></p>
 
 
84
 
85
  <div class="rollback-details">
86
  <table class="widefat">
126
 
127
  </form>
128
 
129
+ </div>
languages/wp-rollback.pot CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2016 WP Rollback
2
  # This file is distributed under the same license as the WP Rollback package.
3
  msgid ""
4
  msgstr ""
@@ -7,7 +7,7 @@ msgstr ""
7
  "MIME-Version: 1.0\n"
8
  "Content-Type: text/plain; charset=UTF-8\n"
9
  "Content-Transfer-Encoding: 8bit\n"
10
- "PO-Revision-Date: 2016-MO-DA HO:MI+ZONE\n"
11
  "Language-Team: WordImpress <info@wordimpress.com>\n"
12
  "X-Poedit-Basepath: ..\n"
13
  "X-Poedit-SourceCharset: UTF-8\n"
@@ -15,90 +15,103 @@ msgstr ""
15
  "X-Poedit-SearchPath-0: .\n"
16
  "X-Poedit-SearchPathExcluded-0: *.js\n"
17
  "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
18
  #: includes/rollback-action.php:35
19
- msgid "This rollback request is a missing proper query string. Please contact support."
20
  msgstr ""
21
 
22
- #: includes/rollback-menu.php:12
23
  msgid "WP Rollback is missing necessary parameters to continue. Please contact support."
24
  msgstr ""
25
 
26
- #: includes/rollback-menu.php:24
27
  msgid "WP Rollback"
28
  msgstr ""
29
 
30
- #: includes/rollback-menu.php:27
31
  msgid "Please select which %1$s version you would like to rollback to from the releases listed below. You currently have version %2$s installed of %3$s."
32
  msgstr ""
33
 
34
- #: includes/rollback-menu.php:27
35
  msgid "theme"
36
  msgstr ""
37
 
38
- #: includes/rollback-menu.php:27
39
  msgid "plugin"
40
  msgstr ""
41
 
42
- #: includes/rollback-menu.php:65, includes/rollback-menu.php:123, wp-rollback.php:244, wp-rollback.php:510, wp-rollback.php:510, wp-rollback.php:572
 
 
 
 
43
  msgid "Rollback"
44
  msgstr ""
45
 
46
- #: includes/rollback-menu.php:66, includes/rollback-menu.php:124
47
  msgid "Cancel"
48
  msgstr ""
49
 
50
- #: includes/rollback-menu.php:84
51
  msgid "Are you sure you want to perform the following rollback?"
52
  msgstr ""
53
 
54
- #: includes/rollback-menu.php:94
55
  msgid "Plugin Name:"
56
  msgstr ""
57
 
58
- #: includes/rollback-menu.php:96
59
  msgid "Theme Name:"
60
  msgstr ""
61
 
62
- #: includes/rollback-menu.php:103
63
  msgid "Installed Version:"
64
  msgstr ""
65
 
66
- #: includes/rollback-menu.php:109
67
  msgid "New Version:"
68
  msgstr ""
69
 
70
- #: includes/rollback-menu.php:119
71
  msgid "<strong>Notice:</strong> We strongly recommend you perform a test rollback on a staging site and create a complete backup of your WordPress files and database prior to performing a rollback. We are not responsible for any misuse, deletions, white screens, fatal errors, or any other issue arising from using this plugin."
72
  msgstr ""
73
 
74
- #: wp-rollback.php:159, wp-rollback.php:171
75
  msgid "Cheatin&#8217; huh?"
76
  msgstr ""
77
 
78
- #: wp-rollback.php:245
79
  msgid "No Rollback Available: This is a non-WordPress.org theme."
80
  msgstr ""
81
 
82
- #: wp-rollback.php:246
83
  msgid "Loading..."
84
  msgstr ""
85
 
86
- #: wp-rollback.php:275
 
 
 
 
87
  msgid "Please select a version number to perform a rollback."
88
  msgstr ""
89
 
90
- #: wp-rollback.php:321
91
  msgid "You do not have sufficient permissions to perform rollbacks for this site."
92
  msgstr ""
93
 
94
- #: wp-rollback.php:452
95
  msgid "Installed Version"
96
  msgstr ""
97
 
98
- #: wp-rollback.php:685
 
 
 
 
99
  msgid "An unexpected error occurred. Something may be wrong with WordPress.org or this server&#8217;s configuration. If you continue to have problems, please try the <a href=\"https://wordpress.org/support/\">support forums</a>."
100
  msgstr ""
101
 
102
- #: wp-rollback.php:685
103
  msgid "(WordPress could not establish a secure connection to WordPress.org. Please contact your server administrator.)"
104
  msgstr ""
1
+ # Copyright (C) 2017 WP Rollback
2
  # This file is distributed under the same license as the WP Rollback package.
3
  msgid ""
4
  msgstr ""
7
  "MIME-Version: 1.0\n"
8
  "Content-Type: text/plain; charset=UTF-8\n"
9
  "Content-Transfer-Encoding: 8bit\n"
10
+ "PO-Revision-Date: 2017-MO-DA HO:MI+ZONE\n"
11
  "Language-Team: WordImpress <info@wordimpress.com>\n"
12
  "X-Poedit-Basepath: ..\n"
13
  "X-Poedit-SourceCharset: UTF-8\n"
15
  "X-Poedit-SearchPath-0: .\n"
16
  "X-Poedit-SearchPathExcluded-0: *.js\n"
17
  "Plural-Forms: nplurals=2; plural=(n != 1);\n"
18
+
19
  #: includes/rollback-action.php:35
20
+ msgid "This rollback request is missing a proper query string. Please contact support."
21
  msgstr ""
22
 
23
+ #: includes/rollback-menu.php:10
24
  msgid "WP Rollback is missing necessary parameters to continue. Please contact support."
25
  msgstr ""
26
 
27
+ #: includes/rollback-menu.php:23
28
  msgid "WP Rollback"
29
  msgstr ""
30
 
31
+ #: includes/rollback-menu.php:26
32
  msgid "Please select which %1$s version you would like to rollback to from the releases listed below. You currently have version %2$s installed of %3$s."
33
  msgstr ""
34
 
35
+ #: includes/rollback-menu.php:26
36
  msgid "theme"
37
  msgstr ""
38
 
39
+ #: includes/rollback-menu.php:26
40
  msgid "plugin"
41
  msgstr ""
42
 
43
+ #: includes/rollback-menu.php:42
44
+ msgid "Oh no! We're missing required rollback query strings. Please contact support so we can check this bug out and squash it!"
45
+ msgstr ""
46
+
47
+ #: includes/rollback-menu.php:64, includes/rollback-menu.php:121, wp-rollback.php:263, wp-rollback.php:567, wp-rollback.php:567, wp-rollback.php:637, wp-rollback.php:688
48
  msgid "Rollback"
49
  msgstr ""
50
 
51
+ #: includes/rollback-menu.php:65, includes/rollback-menu.php:122
52
  msgid "Cancel"
53
  msgstr ""
54
 
55
+ #: includes/rollback-menu.php:83
56
  msgid "Are you sure you want to perform the following rollback?"
57
  msgstr ""
58
 
59
+ #: includes/rollback-menu.php:92
60
  msgid "Plugin Name:"
61
  msgstr ""
62
 
63
+ #: includes/rollback-menu.php:94
64
  msgid "Theme Name:"
65
  msgstr ""
66
 
67
+ #: includes/rollback-menu.php:101
68
  msgid "Installed Version:"
69
  msgstr ""
70
 
71
+ #: includes/rollback-menu.php:107
72
  msgid "New Version:"
73
  msgstr ""
74
 
75
+ #: includes/rollback-menu.php:117
76
  msgid "<strong>Notice:</strong> We strongly recommend you perform a test rollback on a staging site and create a complete backup of your WordPress files and database prior to performing a rollback. We are not responsible for any misuse, deletions, white screens, fatal errors, or any other issue arising from using this plugin."
77
  msgstr ""
78
 
79
+ #: wp-rollback.php:148, wp-rollback.php:160
80
  msgid "Cheatin&#8217; huh?"
81
  msgstr ""
82
 
83
+ #: wp-rollback.php:264
84
  msgid "No Rollback Available: This is a non-WordPress.org theme."
85
  msgstr ""
86
 
87
+ #: wp-rollback.php:265
88
  msgid "Loading..."
89
  msgstr ""
90
 
91
+ #: wp-rollback.php:294
92
+ msgid "Sorry, we couldn't find a changelog entry found for this version. Try checking the <a href=\"%s\" target=\"_blank\">developer log</a> on WP.org."
93
+ msgstr ""
94
+
95
+ #: wp-rollback.php:295
96
  msgid "Please select a version number to perform a rollback."
97
  msgstr ""
98
 
99
+ #: wp-rollback.php:340
100
  msgid "You do not have sufficient permissions to perform rollbacks for this site."
101
  msgstr ""
102
 
103
+ #: wp-rollback.php:503
104
  msgid "Installed Version"
105
  msgstr ""
106
 
107
+ #: wp-rollback.php:510
108
+ msgid "View Changelog"
109
+ msgstr ""
110
+
111
+ #: wp-rollback.php:804
112
  msgid "An unexpected error occurred. Something may be wrong with WordPress.org or this server&#8217;s configuration. If you continue to have problems, please try the <a href=\"https://wordpress.org/support/\">support forums</a>."
113
  msgstr ""
114
 
115
+ #: wp-rollback.php:804
116
  msgid "(WordPress could not establish a secure connection to WordPress.org. Please contact your server administrator.)"
117
  msgstr ""
license.txt DELETED
@@ -1,281 +0,0 @@
1
- GNU GENERAL PUBLIC LICENSE
2
- Version 2, June 1991
3
-
4
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
5
- 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
6
-
7
- Everyone is permitted to copy and distribute verbatim copies
8
- of this license document, but changing it is not allowed.
9
-
10
- Preamble
11
-
12
- The licenses for most software are designed to take away your
13
- freedom to share and change it. By contrast, the GNU General Public
14
- License is intended to guarantee your freedom to share and change free
15
- software--to make sure the software is free for all its users. This
16
- General Public License applies to most of the Free Software
17
- Foundation's software and to any other program whose authors commit to
18
- using it. (Some other Free Software Foundation software is covered by
19
- the GNU Library General Public License instead.) You can apply it to
20
- your programs, too.
21
-
22
- When we speak of free software, we are referring to freedom, not
23
- price. Our General Public Licenses are designed to make sure that you
24
- have the freedom to distribute copies of free software (and charge for
25
- this service if you wish), that you receive source code or can get it
26
- if you want it, that you can change the software or use pieces of it
27
- in new free programs; and that you know you can do these things.
28
-
29
- To protect your rights, we need to make restrictions that forbid
30
- anyone to deny you these rights or to ask you to surrender the rights.
31
- These restrictions translate to certain responsibilities for you if you
32
- distribute copies of the software, or if you modify it.
33
-
34
- For example, if you distribute copies of such a program, whether
35
- gratis or for a fee, you must give the recipients all the rights that
36
- you have. You must make sure that they, too, receive or can get the
37
- source code. And you must show them these terms so they know their
38
- rights.
39
-
40
- We protect your rights with two steps: (1) copyright the software, and
41
- (2) offer you this license which gives you legal permission to copy,
42
- distribute and/or modify the software.
43
-
44
- Also, for each author's protection and ours, we want to make certain
45
- that everyone understands that there is no warranty for this free
46
- software. If the software is modified by someone else and passed on, we
47
- want its recipients to know that what they have is not the original, so
48
- that any problems introduced by others will not reflect on the original
49
- authors' reputations.
50
-
51
- Finally, any free program is threatened constantly by software
52
- patents. We wish to avoid the danger that redistributors of a free
53
- program will individually obtain patent licenses, in effect making the
54
- program proprietary. To prevent this, we have made it clear that any
55
- patent must be licensed for everyone's free use or not licensed at all.
56
-
57
- The precise terms and conditions for copying, distribution and
58
- modification follow.
59
-
60
- GNU GENERAL PUBLIC LICENSE
61
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
62
-
63
- 0. This License applies to any program or other work which contains
64
- a notice placed by the copyright holder saying it may be distributed
65
- under the terms of this General Public License. The "Program", below,
66
- refers to any such program or work, and a "work based on the Program"
67
- means either the Program or any derivative work under copyright law:
68
- that is to say, a work containing the Program or a portion of it,
69
- either verbatim or with modifications and/or translated into another
70
- language. (Hereinafter, translation is included without limitation in
71
- the term "modification".) Each licensee is addressed as "you".
72
-
73
- Activities other than copying, distribution and modification are not
74
- covered by this License; they are outside its scope. The act of
75
- running the Program is not restricted, and the output from the Program
76
- is covered only if its contents constitute a work based on the
77
- Program (independent of having been made by running the Program).
78
- Whether that is true depends on what the Program does.
79
-
80
- 1. You may copy and distribute verbatim copies of the Program's
81
- source code as you receive it, in any medium, provided that you
82
- conspicuously and appropriately publish on each copy an appropriate
83
- copyright notice and disclaimer of warranty; keep intact all the
84
- notices that refer to this License and to the absence of any warranty;
85
- and give any other recipients of the Program a copy of this License
86
- along with the Program.
87
-
88
- You may charge a fee for the physical act of transferring a copy, and
89
- you may at your option offer warranty protection in exchange for a fee.
90
-
91
- 2. You may modify your copy or copies of the Program or any portion
92
- of it, thus forming a work based on the Program, and copy and
93
- distribute such modifications or work under the terms of Section 1
94
- above, provided that you also meet all of these conditions:
95
-
96
- a) You must cause the modified files to carry prominent notices
97
- stating that you changed the files and the date of any change.
98
-
99
- b) You must cause any work that you distribute or publish, that in
100
- whole or in part contains or is derived from the Program or any
101
- part thereof, to be licensed as a whole at no charge to all third
102
- parties under the terms of this License.
103
-
104
- c) If the modified program normally reads commands interactively
105
- when run, you must cause it, when started running for such
106
- interactive use in the most ordinary way, to print or display an
107
- announcement including an appropriate copyright notice and a
108
- notice that there is no warranty (or else, saying that you provide
109
- a warranty) and that users may redistribute the program under
110
- these conditions, and telling the user how to view a copy of this
111
- License. (Exception: if the Program itself is interactive but
112
- does not normally print such an announcement, your work based on
113
- the Program is not required to print an announcement.)
114
-
115
- These requirements apply to the modified work as a whole. If
116
- identifiable sections of that work are not derived from the Program,
117
- and can be reasonably considered independent and separate works in
118
- themselves, then this License, and its terms, do not apply to those
119
- sections when you distribute them as separate works. But when you
120
- distribute the same sections as part of a whole which is a work based
121
- on the Program, the distribution of the whole must be on the terms of
122
- this License, whose permissions for other licensees extend to the
123
- entire whole, and thus to each and every part regardless of who wrote it.
124
- Thus, it is not the intent of this section to claim rights or contest
125
- your rights to work written entirely by you; rather, the intent is to
126
- exercise the right to control the distribution of derivative or
127
- collective works based on the Program.
128
-
129
- In addition, mere aggregation of another work not based on the Program
130
- with the Program (or with a work based on the Program) on a volume of
131
- a storage or distribution medium does not bring the other work under
132
- the scope of this License.
133
-
134
- 3. You may copy and distribute the Program (or a work based on it,
135
- under Section 2) in object code or executable form under the terms of
136
- Sections 1 and 2 above provided that you also do one of the following:
137
-
138
- a) Accompany it with the complete corresponding machine-readable
139
- source code, which must be distributed under the terms of Sections
140
- 1 and 2 above on a medium customarily used for software interchange; or,
141
-
142
- b) Accompany it with a written offer, valid for at least three
143
- years, to give any third party, for a charge no more than your
144
- cost of physically performing source distribution, a complete
145
- machine-readable copy of the corresponding source code, to be
146
- distributed under the terms of Sections 1 and 2 above on a medium
147
- customarily used for software interchange; or,
148
-
149
- c) Accompany it with the information you received as to the offer
150
- to distribute corresponding source code. (This alternative is
151
- allowed only for noncommercial distribution and only if you
152
- received the program in object code or executable form with such
153
- an offer, in accord with Subsection b above.)
154
-
155
- The source code for a work means the preferred form of the work for
156
- making modifications to it. For an executable work, complete source
157
- code means all the source code for all modules it contains, plus any
158
- associated interface definition files, plus the scripts used to
159
- control compilation and installation of the executable. However, as a
160
- special exception, the source code distributed need not include
161
- anything that is normally distributed (in either source or binary
162
- form) with the major components (compiler, kernel, and so on) of the
163
- operating system on which the executable runs, unless that component
164
- itself accompanies the executable.
165
-
166
- If distribution of executable or object code is made by offering
167
- access to copy from a designated place, then offering equivalent
168
- access to copy the source code from the same place counts as
169
- distribution of the source code, even though third parties are not
170
- compelled to copy the source along with the object code.
171
-
172
- 4. You may not copy, modify, sublicense, or distribute the Program
173
- except as expressly provided under this License. Any attempt
174
- otherwise to copy, modify, sublicense or distribute the Program is
175
- void, and will automatically terminate your rights under this License.
176
- However, parties who have received copies, or rights, from you under
177
- this License will not have their licenses terminated so long as such
178
- parties remain in full compliance.
179
-
180
- 5. You are not required to accept this License, since you have not
181
- signed it. However, nothing else grants you permission to modify or
182
- distribute the Program or its derivative works. These actions are
183
- prohibited by law if you do not accept this License. Therefore, by
184
- modifying or distributing the Program (or any work based on the
185
- Program), you indicate your acceptance of this License to do so, and
186
- all its terms and conditions for copying, distributing or modifying
187
- the Program or works based on it.
188
-
189
- 6. Each time you redistribute the Program (or any work based on the
190
- Program), the recipient automatically receives a license from the
191
- original licensor to copy, distribute or modify the Program subject to
192
- these terms and conditions. You may not impose any further
193
- restrictions on the recipients' exercise of the rights granted herein.
194
- You are not responsible for enforcing compliance by third parties to
195
- this License.
196
-
197
- 7. If, as a consequence of a court judgment or allegation of patent
198
- infringement or for any other reason (not limited to patent issues),
199
- conditions are imposed on you (whether by court order, agreement or
200
- otherwise) that contradict the conditions of this License, they do not
201
- excuse you from the conditions of this License. If you cannot
202
- distribute so as to satisfy simultaneously your obligations under this
203
- License and any other pertinent obligations, then as a consequence you
204
- may not distribute the Program at all. For example, if a patent
205
- license would not permit royalty-free redistribution of the Program by
206
- all those who receive copies directly or indirectly through you, then
207
- the only way you could satisfy both it and this License would be to
208
- refrain entirely from distribution of the Program.
209
-
210
- If any portion of this section is held invalid or unenforceable under
211
- any particular circumstance, the balance of the section is intended to
212
- apply and the section as a whole is intended to apply in other
213
- circumstances.
214
-
215
- It is not the purpose of this section to induce you to infringe any
216
- patents or other property right claims or to contest validity of any
217
- such claims; this section has the sole purpose of protecting the
218
- integrity of the free software distribution system, which is
219
- implemented by public license practices. Many people have made
220
- generous contributions to the wide range of software distributed
221
- through that system in reliance on consistent application of that
222
- system; it is up to the author/donor to decide if he or she is willing
223
- to distribute software through any other system and a licensee cannot
224
- impose that choice.
225
-
226
- This section is intended to make thoroughly clear what is believed to
227
- be a consequence of the rest of this License.
228
-
229
- 8. If the distribution and/or use of the Program is restricted in
230
- certain countries either by patents or by copyrighted interfaces, the
231
- original copyright holder who places the Program under this License
232
- may add an explicit geographical distribution limitation excluding
233
- those countries, so that distribution is permitted only in or among
234
- countries not thus excluded. In such case, this License incorporates
235
- the limitation as if written in the body of this License.
236
-
237
- 9. The Free Software Foundation may publish revised and/or new versions
238
- of the General Public License from time to time. Such new versions will
239
- be similar in spirit to the present version, but may differ in detail to
240
- address new problems or concerns.
241
-
242
- Each version is given a distinguishing version number. If the Program
243
- specifies a version number of this License which applies to it and "any
244
- later version", you have the option of following the terms and conditions
245
- either of that version or of any later version published by the Free
246
- Software Foundation. If the Program does not specify a version number of
247
- this License, you may choose any version ever published by the Free Software
248
- Foundation.
249
-
250
- 10. If you wish to incorporate parts of the Program into other free
251
- programs whose distribution conditions are different, write to the author
252
- to ask for permission. For software which is copyrighted by the Free
253
- Software Foundation, write to the Free Software Foundation; we sometimes
254
- make exceptions for this. Our decision will be guided by the two goals
255
- of preserving the free status of all derivatives of our free software and
256
- of promoting the sharing and reuse of software generally.
257
-
258
- NO WARRANTY
259
-
260
- 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261
- FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262
- OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263
- PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264
- OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265
- MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266
- TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267
- PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268
- REPAIR OR CORRECTION.
269
-
270
- 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271
- WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272
- REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273
- INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274
- OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275
- TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276
- YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277
- PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278
- POSSIBILITY OF SUCH DAMAGES.
279
-
280
- END OF TERMS AND CONDITIONS
281
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
readme.txt CHANGED
@@ -3,8 +3,8 @@ Contributors: wordimpress, dlocc, drrobotnik, webdevmattcrom
3
  Tags: rollback, revert, downgrade, version, plugins, themes, version, versions, backup, backups, revision, revisions
4
  Requires at least: 4.0
5
  Donate Link: https://wordimpress.com
6
- Tested up to: 4.6.1
7
- Stable tag: 1.4
8
  License: GPLv3
9
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
10
 
@@ -85,7 +85,7 @@ The documentation for this plugin is located on our [Github Wiki](https://github
85
 
86
  Yes! All strings are internationalized and ready to be translated. Simply use the languages/wp-rollback.pot file and your favorite translation tool. Once finished, please reach out to us on the WordPress.org forums or better yet, submit a pull request on the [Github Repo](https://github.com/WordImpress/WP-Rollback/).
87
 
88
- = Did this plugin with WordCamp Orange County's Plugin-Palooza? =
89
 
90
  Heck yes it did! The WordImpress team took home the gold.
91
 
@@ -111,19 +111,25 @@ This is the first version of this plugin. It is a tool for your convenience. Rol
111
 
112
  == Changelog ==
113
 
 
 
 
 
 
 
114
  = 1.4 =
115
- * New: Updated plugin's text domain to the plugin's slug of 'wp-rollback' to support WordPress' GlotPress translations - https://github.com/WordImpress/WP-Rollback/issues/28
116
- * New: Gulp automated POT file generation and text domain checker - https://github.com/WordImpress/WP-Rollback/issues/28
117
- * Fix: Check the WP install's themes transient is present, if not fetch it to see if a theme can be rolled back. Allows rollbacks for new WP installs or in a case where the transient is not set properly - https://github.com/WordImpress/WP-Rollback/issues/27
118
 
119
  = 1.3 =
120
  * Tested compatibility with WordPress 4.4 and verified as working; bumped up compatibility
121
- * Fix: Trying to get property of non-object warning - https://github.com/WordImpress/WP-Rollback/issues/20
122
- * Improvement: Better version sorting now using usort & version_compare - https://github.com/WordImpress/WP-Rollback/issues/16
123
 
124
  = 1.2.4 =
125
- * New: Portuguese translations added
126
- * Fix: Limit HTTP requests to Plugin page only #17 @see: https://wordpress.org/support/topic/great-plugin-but-small-issue?replies=5 and https://wordpress.org/support/topic/great-plugin-but-small-issue?replies=1#post-7234287
127
 
128
  = 1.2.3 =
129
  * Fixed: XSS hardening. Thanks @secupress
@@ -132,17 +138,17 @@ This is the first version of this plugin. It is a tool for your convenience. Rol
132
 
133
  = 1.2.2 =
134
  * New: Russian translations from @Flector - thanks!
135
- * Fix: Replaced use of wp_json_encode to support older WordPress versions @see https://wordpress.org/support/topic/wordpress-requirement-issue-with-wp_json_encode
136
 
137
  = 1.2.1 =
138
- * Fix: Rollback link appears on non wp.org plugins https://github.com/WordImpress/WP-Rollback/issues/14 - thanks @scottopolis
139
- * Removed unnecessary WP_ROLLBACK_VERSION constant
140
 
141
  = 1.2 =
142
- * New: Swedish translation files - Thanks @WPDailyThemes
143
 
144
  = 1.1 =
145
- * Fixed "Cancel" button which was falsely submitting the form
146
 
147
  = 1.0 =
148
 
3
  Tags: rollback, revert, downgrade, version, plugins, themes, version, versions, backup, backups, revision, revisions
4
  Requires at least: 4.0
5
  Donate Link: https://wordimpress.com
6
+ Tested up to: 4.8
7
+ Stable tag: 1.5
8
  License: GPLv3
9
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
10
 
85
 
86
  Yes! All strings are internationalized and ready to be translated. Simply use the languages/wp-rollback.pot file and your favorite translation tool. Once finished, please reach out to us on the WordPress.org forums or better yet, submit a pull request on the [Github Repo](https://github.com/WordImpress/WP-Rollback/).
87
 
88
+ = Did this plugin win WordCamp Orange County's Plugin-Palooza? =
89
 
90
  Heck yes it did! The WordImpress team took home the gold.
91
 
111
 
112
  == Changelog ==
113
 
114
+ = 1.5 =
115
+ * New: You can now view plugin changelogs within the rollback screen. [#7](https://github.com/WordImpress/WP-Rollback/issues/7)
116
+ * New: Added support for WordPress Multisite rollbacks for themes and plugins. [#22](https://github.com/WordImpress/WP-Rollback/issues/22)
117
+ * New: Rollback button is fixed to the bottom of the page now to prevent long scrolls for rollbacks with many versions. [#23](https://github.com/WordImpress/WP-Rollback/issues/23)
118
+ * New: Updated the WP.org plugin header graphic. [#37](https://github.com/WordImpress/WP-Rollback/issues/37)
119
+
120
  = 1.4 =
121
+ * New: Updated plugin's text domain to the plugin's slug of 'wp-rollback' to support WordPress' GlotPress translations. [#28](https://github.com/WordImpress/WP-Rollback/issues/28)
122
+ * New: Gulp automated POT file generation and text domain checker. [#28](https://github.com/WordImpress/WP-Rollback/issues/28)
123
+ * Fix: Check the WP install's themes transient is present, if not fetch it to see if a theme can be rolled back. Allows rollbacks for new WP installs or in a case where the transient is not set properly.[#27](https://github.com/WordImpress/WP-Rollback/issues/27)
124
 
125
  = 1.3 =
126
  * Tested compatibility with WordPress 4.4 and verified as working; bumped up compatibility
127
+ * Fix: Trying to get property of non-object warning. [#20](https://github.com/WordImpress/WP-Rollback/issues/20)
128
+ * Improvement: Better version sorting now using usort & version_compare. [#16](https://github.com/WordImpress/WP-Rollback/issues/16)
129
 
130
  = 1.2.4 =
131
+ * New: Portuguese translations added.
132
+ * Fix: Limit HTTP requests to Plugin page only. [Report 1](https://wordpress.org/support/topic/great-plugin-but-small-issue?replies=5) [Report 2](https://wordpress.org/support/topic/great-plugin-but-small-issue?replies=1#post-7234287)
133
 
134
  = 1.2.3 =
135
  * Fixed: XSS hardening. Thanks @secupress
138
 
139
  = 1.2.2 =
140
  * New: Russian translations from @Flector - thanks!
141
+ * Fix: Replaced use of wp_json_encode to support older WordPress versions. [Report](https://wordpress.org/support/topic/wordpress-requirement-issue-with-wp_json_encode)
142
 
143
  = 1.2.1 =
144
+ * Fix: Rollback link appears on non wp.org plugins - thanks @scottopolis. [#14](https://github.com/WordImpress/WP-Rollback/issues/14)
145
+ * Removed unnecessary WP_ROLLBACK_VERSION constant.
146
 
147
  = 1.2 =
148
+ * New: Swedish translation files - Thanks @WPDailyThemes.
149
 
150
  = 1.1 =
151
+ * Fixed "Cancel" button which was falsely submitting the form.
152
 
153
  = 1.0 =
154
 
wp-rollback.php CHANGED
@@ -1,11 +1,11 @@
1
  <?php
2
  /**
3
  * Plugin Name: WP Rollback
4
- * Plugin URI: https://wordimpress.com
5
  * Description: Rollback (or forward) any WordPress.org plugin or theme like a boss.
6
  * Author: WordImpress
7
- * Author URI: https://wordimpress.com
8
- * Version: 1.4
9
  * Text Domain: wp-rollback
10
  * Domain Path: /languages
11
  *
@@ -35,13 +35,15 @@ if ( ! defined( 'ABSPATH' ) ) {
35
  * @since 1.0
36
  */
37
  if ( ! class_exists( 'WP Rollback' ) ) : {
 
38
  /**
39
  * Class WP_Rollback
40
  */
41
  final class WP_Rollback {
42
- /** Singleton *************************************************************/
43
 
44
  /**
 
 
45
  * @var WP_Rollback The one and only
46
  * @since 1.0
47
  */
@@ -64,6 +66,7 @@ if ( ! class_exists( 'WP Rollback' ) ) : {
64
 
65
  /**
66
  * Themes repo url.
 
67
  * @var string
68
  */
69
  public $themes_repo = 'http://themes.svn.wordpress.org';
@@ -117,26 +120,12 @@ if ( ! class_exists( 'WP Rollback' ) ) : {
117
  self::$instance = new WP_Rollback;
118
  self::$instance->setup_constants();
119
 
120
- //Only setup plugin rollback on specific page
121
  if ( isset( $_GET['plugin_file'] ) && $_GET['page'] == 'wp-rollback' ) {
122
  self::$instance->setup_plugin_vars();
123
  }
124
 
125
- //i18n
126
- add_action( 'plugins_loaded', array( self::$instance, 'load_textdomain' ) );
127
- //Admin
128
- add_action( 'admin_enqueue_scripts', array( self::$instance, 'scripts' ) );
129
- add_action( 'admin_menu', array( self::$instance, 'admin_menu' ), 20 );
130
- add_action( 'pre_current_active_plugins', array(
131
- self::$instance,
132
- 'pre_current_active_plugins'
133
- ), 20, 1 );
134
- add_action( 'wp_ajax_is_wordpress_theme', array( self::$instance, 'is_wordpress_theme' ) );
135
- add_action( 'set_site_transient_update_themes', array( self::$instance, 'wpr_theme_updates_list' ) );
136
-
137
- add_filter( 'wp_prepare_themes_for_js', array( self::$instance, 'wpr_prepare_themes_js' ) );
138
- add_filter( 'plugin_action_links', array( self::$instance, 'plugin_action_links' ), 20, 4 );
139
-
140
  self::$instance->includes();
141
 
142
  }
@@ -210,6 +199,37 @@ if ( ! class_exists( 'WP Rollback' ) ) : {
210
  $this->set_svn_versions_data( $svn_tags );
211
  }
212
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
  /**
214
  * Include required files
215
  *
@@ -230,13 +250,12 @@ if ( ! class_exists( 'WP Rollback' ) ) : {
230
  * @param $hook
231
  *
232
  * @return void
233
- *
234
  */
235
  public function scripts( $hook ) {
236
 
237
- if ( $hook === 'themes.php' ) {
238
  wp_enqueue_script( 'wp_rollback_themes_script', plugin_dir_url( __FILE__ ) . 'assets/js/themes-wp-rollback.js', array( 'jquery' ), false, true );
239
- //Localize for i18n
240
  wp_localize_script( 'wp_rollback_themes_script', 'wpr_vars', array(
241
  'ajaxurl' => admin_url(),
242
  'ajax_loader' => admin_url( 'images/spinner.gif' ),
@@ -247,20 +266,20 @@ if ( ! class_exists( 'WP Rollback' ) ) : {
247
  ) );
248
  }
249
 
250
- if ( $hook !== 'dashboard_page_wp-rollback' ) {
251
  return;
252
  }
253
 
254
  $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
255
 
256
- //CSS
257
  wp_register_style( 'wp_rollback_css', plugin_dir_url( __FILE__ ) . 'assets/css/wp-rollback.css' );
258
  wp_enqueue_style( 'wp_rollback_css' );
259
 
260
  wp_register_style( 'wp_rollback_modal_css', plugin_dir_url( __FILE__ ) . 'assets/css/magnific-popup.css' );
261
  wp_enqueue_style( 'wp_rollback_modal_css' );
262
 
263
- //JS
264
  wp_register_script( 'wp_rollback_modal', plugin_dir_url( __FILE__ ) . 'assets/js/jquery.magnific-popup' . $suffix . '.js', array( 'jquery' ) );
265
  wp_enqueue_script( 'wp_rollback_modal' );
266
 
@@ -269,13 +288,13 @@ if ( ! class_exists( 'WP Rollback' ) ) : {
269
 
270
  wp_enqueue_script( 'updates' );
271
 
272
- //Localize for i18n.
273
  wp_localize_script( 'wp_rollback_script', 'wpr_vars', array(
274
- 'ajaxurl' => admin_url(),
275
- 'version_missing' => __( 'Please select a version number to perform a rollback.', 'wp-rollback' ),
 
276
  ) );
277
 
278
-
279
  }
280
 
281
  /**
@@ -316,12 +335,12 @@ if ( ! class_exists( 'WP Rollback' ) ) : {
316
  */
317
  public function html() {
318
 
319
- //Permissions check
320
  if ( ! current_user_can( 'update_plugins' ) ) {
321
  wp_die( __( 'You do not have sufficient permissions to perform rollbacks for this site.', 'wp-rollback' ) );
322
  }
323
 
324
- //Get the necessary class
325
  include_once( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' );
326
 
327
  $defaults = apply_filters( 'wpr_rollback_html_args', array(
@@ -329,24 +348,23 @@ if ( ! class_exists( 'WP Rollback' ) ) : {
329
  'plugin_file' => '',
330
  'action' => '',
331
  'plugin_version' => '',
332
- 'plugin' => ''
333
  ) );
334
 
335
-
336
  $args = wp_parse_args( $_GET, $defaults );
337
 
338
  if ( ! empty( $args['plugin_version'] ) ) {
339
- //Plugin: rolling back.
340
  check_admin_referer( 'wpr_rollback_nonce' );
341
  include WP_ROLLBACK_PLUGIN_DIR . '/includes/class-rollback-plugin-upgrader.php';
342
  include WP_ROLLBACK_PLUGIN_DIR . '/includes/rollback-action.php';
343
  } elseif ( ! empty( $args['theme_version'] ) ) {
344
- //Theme: rolling back.
345
  check_admin_referer( 'wpr_rollback_nonce' );
346
  include WP_ROLLBACK_PLUGIN_DIR . '/includes/class-rollback-theme-upgrader.php';
347
  include WP_ROLLBACK_PLUGIN_DIR . '/includes/rollback-action.php';
348
  } else {
349
- //This is the menu.
350
  check_admin_referer( 'wpr_rollback_nonce' );
351
  include WP_ROLLBACK_PLUGIN_DIR . '/includes/rollback-menu.php';
352
 
@@ -355,33 +373,66 @@ if ( ! class_exists( 'WP Rollback' ) ) : {
355
  }
356
 
357
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
358
  /**
359
  * Get Subversion Tags
360
  *
361
- * @description cURLs wp.org repo to get the proper tags
362
  *
363
  * @param $type
364
  * @param $slug
365
  *
366
  * @return null|string
367
  */
368
- private function get_svn_tags( $type, $slug ) {
369
 
370
  $url = $this->plugins_repo . '/' . $this->plugin_slug . '/tags/';
371
 
372
- //is this a theme svn request?
373
- if ( $type == 'theme' ) {
374
  $url = $this->themes_repo . '/' . $slug;
375
  }
376
 
377
  $response = wp_remote_get( $url );
378
 
379
- //Do we have an error?
380
  if ( wp_remote_retrieve_response_code( $response ) !== 200 ) {
381
  return null;
382
  }
383
 
384
- //Nope: Return that bad boy
385
  return wp_remote_retrieve_body( $response );
386
 
387
  }
@@ -389,13 +440,11 @@ if ( ! class_exists( 'WP Rollback' ) ) : {
389
  /**
390
  * Set SVN Version Data
391
  *
392
- * @description FILL ME IN
393
- *
394
  * @param $html
395
  *
396
  * @return array|bool
397
  */
398
- private function set_svn_versions_data( $html ) {
399
 
400
  if ( ! $html ) {
401
  return false;
@@ -409,9 +458,9 @@ if ( ! class_exists( 'WP Rollback' ) ) : {
409
  $items = $DOM->getElementsByTagName( 'a' );
410
 
411
  foreach ( $items as $item ) {
412
- $href = str_replace( '/', '', $item->getAttribute( 'href' ) ); //Remove trailing slash
413
 
414
- if ( strpos( $href, 'http' ) === false && $href !== '..' ) {
415
  $versions[] = $href;
416
  }
417
  }
@@ -436,34 +485,44 @@ if ( ! class_exists( 'WP Rollback' ) ) : {
436
  return false;
437
  }
438
 
439
- $versions_html = '';
440
 
441
  usort( $this->versions, 'version_compare' );
442
 
443
  $this->versions = array_reverse( $this->versions );
444
 
445
- //Loop through versions and output in a radio list
 
446
  foreach ( $this->versions as $version ) {
447
 
 
448
  $versions_html .= '<label><input type="radio" value="' . esc_attr( $version ) . '" name="' . $type . '_version">' . $version;
449
 
450
- //Is this the current version?
451
  if ( $version === $this->current_version ) {
452
  $versions_html .= '<span class="current-version">' . __( 'Installed Version', 'wp-rollback' ) . '</span>';
453
  }
454
 
455
  $versions_html .= '</label>';
456
 
 
 
 
 
 
 
457
 
458
  }
459
 
460
- return $versions_html;
 
 
461
  }
462
 
463
  /**
464
  * Set Plugin Slug
465
  *
466
- * @return bool
467
  */
468
  private function set_plugin_slug() {
469
 
@@ -484,9 +543,7 @@ if ( ! class_exists( 'WP Rollback' ) ) : {
484
  wp_die( 'Plugin you\'re referencing does not exist.' );
485
  }
486
 
487
- $plugin_data = get_plugin_data( $plugin_file, false, false );
488
-
489
- //the plugin slug is the base directory name without the path to the main file
490
  $plugin_slug = explode( '/', plugin_basename( $plugin_file ) );
491
 
492
  $this->plugin_file = apply_filters( 'wpr_plugin_file', $plugin_file );
@@ -503,13 +560,13 @@ if ( ! class_exists( 'WP Rollback' ) ) : {
503
  */
504
  public function admin_menu() {
505
 
506
- //Only show menu item when necessary (user is interacting with plugin, ie rolling back something)
507
  if ( isset( $_GET['page'] ) && $_GET['page'] == 'wp-rollback' ) {
508
 
509
- //Add it in a native WP way, like WP updates do... (a dashboard page)
510
  add_dashboard_page( __( 'Rollback', 'wp-rollback' ), __( 'Rollback', 'wp-rollback' ), 'update_plugins', 'wp-rollback', array(
511
  self::$instance,
512
- 'html'
513
  ) );
514
 
515
  }
@@ -548,27 +605,35 @@ if ( ! class_exists( 'WP Rollback' ) ) : {
548
  */
549
  public function plugin_action_links( $actions, $plugin_file, $plugin_data, $context ) {
550
 
551
- //Base rollback URL
552
- $rollback_url = 'index.php?page=wp-rollback&type=plugin&plugin_file=' . $plugin_file;
553
-
554
- //Filter for other devs
555
  $plugin_data = apply_filters( 'wpr_plugin_data', $plugin_data );
556
 
557
- //If plugin is missing package data do not output Rollback option
558
  if ( ! isset( $plugin_data['package'] ) || strpos( $plugin_data['package'], 'https://downloads.wordpress.org' ) === false ) {
559
  return $actions;
560
  }
561
 
562
- //Add in the current version for later reference
563
- if ( isset( $plugin_data['Version'] ) ) {
564
- $rollback_url = add_query_arg( apply_filters( 'wpr_plugin_query_args', array(
565
- 'current_version' => urlencode( $plugin_data['Version'] ),
566
- 'rollback_name' => urlencode( $plugin_data['Name'] ),
567
- '_wpnonce' => wp_create_nonce( 'wpr_rollback_nonce' )
568
- ) ), $rollback_url );
 
569
  }
570
 
571
- //Final Output
 
 
 
 
 
 
 
 
 
 
572
  $actions['rollback'] = apply_filters( 'wpr_plugin_markup', '<a href="' . esc_url( $rollback_url ) . '">' . __( 'Rollback', 'wp-rollback' ) . '</a>' );
573
 
574
  return apply_filters( 'wpr_plugin_action_links', $actions );
@@ -576,16 +641,70 @@ if ( ! class_exists( 'WP Rollback' ) ) : {
576
  }
577
 
578
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
579
  /**
580
  * Is WordPress Theme?
581
  *
582
  * Queries the WordPress.org API via theme's slug to see if this theme is on WordPress.
583
  *
584
  * @return bool
585
- * @TODO Set transient here to speed up future checks?
586
  */
587
  public function is_wordpress_theme() {
588
 
 
 
 
 
 
589
  $url = add_query_arg( 'request[slug]', $_POST['theme'], 'https://api.wordpress.org/themes/info/1.1/?action=theme_information' );
590
  $wp_api = wp_remote_get( $url );
591
 
@@ -602,8 +721,6 @@ if ( ! class_exists( 'WP Rollback' ) ) : {
602
  // Die is required to terminate immediately and return a proper response.
603
  wp_die();
604
 
605
- return true;
606
-
607
  }
608
 
609
 
@@ -631,7 +748,7 @@ if ( ! class_exists( 'WP Rollback' ) ) : {
631
 
632
  include( ABSPATH . WPINC . '/version.php' ); // include an unmodified $wp_version
633
 
634
- //Bounce out if improperly called.
635
  if ( defined( 'WP_INSTALLING' ) || ! is_admin() ) {
636
  return false;
637
  }
@@ -667,12 +784,14 @@ if ( ! class_exists( 'WP Rollback' ) ) : {
667
 
668
  $timeout = 3 + (int) ( count( $themes ) / 10 );
669
 
 
 
670
  $options = array(
671
  'timeout' => $timeout,
672
  'body' => array(
673
  'themes' => json_encode( $request ),
674
  ),
675
- 'user-agent' => 'WordPress/' . $wp_version . '; ' . get_bloginfo( 'url' )
676
  );
677
 
678
  $url = $http_url = 'http://api.wordpress.org/themes/update-check/1.1/';
@@ -698,7 +817,7 @@ if ( ! class_exists( 'WP Rollback' ) ) : {
698
 
699
  $response = json_decode( wp_remote_retrieve_body( $raw_response ), true );
700
 
701
- if ( is_array( $response ) && isset($response['themes'])) {
702
  $new_update->response = $response['themes'];
703
  }
704
 
@@ -721,18 +840,18 @@ if ( ! class_exists( 'WP Rollback' ) ) : {
721
  $rollbacks = array();
722
  $wp_themes = get_site_transient( 'rollback_themes' );
723
 
724
- //Double check our transient is present.
725
- if(empty($wp_themes) || !is_object($wp_themes)) {
726
  $this->wpr_theme_updates_list();
727
  $wp_themes = get_site_transient( 'rollback_themes' );
728
  }
729
 
730
- //Set $rollback response variable for loop ahead.
731
  if ( is_object( $wp_themes ) ) {
732
  $rollbacks = $wp_themes->response;
733
  }
734
 
735
- //Loop through themes and provide a 'hasRollback' boolean key for JS.
736
  foreach ( $prepared_themes as $key => $value ) {
737
  $themes[ $key ] = $prepared_themes[ $key ];
738
  $themes[ $key ]['hasRollback'] = isset( $rollbacks[ $key ] );
@@ -741,7 +860,6 @@ if ( ! class_exists( 'WP Rollback' ) ) : {
741
  return $themes;
742
  }
743
 
744
-
745
  }
746
  }
747
 
@@ -758,11 +876,11 @@ endif; // End if class_exists check
758
  * Example: <?php $wp_rollback = WP_Rollback(); ?>
759
  *
760
  * @since 1.0
761
- * @return object The one true WP Rollback Instance
762
  */
763
  function WP_Rollback() {
764
  return WP_Rollback::instance();
765
  }
766
 
767
  // Get WP Rollback Running
768
- WP_Rollback();
1
  <?php
2
  /**
3
  * Plugin Name: WP Rollback
4
+ * Plugin URI: https://wordimpress.com/
5
  * Description: Rollback (or forward) any WordPress.org plugin or theme like a boss.
6
  * Author: WordImpress
7
+ * Author URI: https://wordimpress.com/
8
+ * Version: 1.5
9
  * Text Domain: wp-rollback
10
  * Domain Path: /languages
11
  *
35
  * @since 1.0
36
  */
37
  if ( ! class_exists( 'WP Rollback' ) ) : {
38
+
39
  /**
40
  * Class WP_Rollback
41
  */
42
  final class WP_Rollback {
 
43
 
44
  /**
45
+ * WP_Rollback instance
46
+ *
47
  * @var WP_Rollback The one and only
48
  * @since 1.0
49
  */
66
 
67
  /**
68
  * Themes repo url.
69
+ *
70
  * @var string
71
  */
72
  public $themes_repo = 'http://themes.svn.wordpress.org';
120
  self::$instance = new WP_Rollback;
121
  self::$instance->setup_constants();
122
 
123
+ // Only setup plugin rollback on specific page
124
  if ( isset( $_GET['plugin_file'] ) && $_GET['page'] == 'wp-rollback' ) {
125
  self::$instance->setup_plugin_vars();
126
  }
127
 
128
+ self::$instance->hooks();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  self::$instance->includes();
130
 
131
  }
199
  $this->set_svn_versions_data( $svn_tags );
200
  }
201
 
202
+ /**
203
+ * Plugin hooks.
204
+ *
205
+ * @access private
206
+ * @since 1.5
207
+ * @return void
208
+ */
209
+ private function hooks() {
210
+ // i18n
211
+ add_action( 'plugins_loaded', array( self::$instance, 'load_textdomain' ) );
212
+ // Admin
213
+ add_action( 'admin_enqueue_scripts', array( self::$instance, 'scripts' ) );
214
+ add_action( 'admin_menu', array( self::$instance, 'admin_menu' ), 20 );
215
+ add_action( 'pre_current_active_plugins', array(
216
+ self::$instance,
217
+ 'pre_current_active_plugins',
218
+ ), 20, 1 );
219
+ add_action( 'wp_ajax_is_wordpress_theme', array( self::$instance, 'is_wordpress_theme' ) );
220
+ add_action( 'set_site_transient_update_themes', array( self::$instance, 'wpr_theme_updates_list' ) );
221
+
222
+ add_filter( 'wp_prepare_themes_for_js', array( self::$instance, 'wpr_prepare_themes_js' ) );
223
+ add_filter( 'plugin_action_links', array( self::$instance, 'plugin_action_links' ), 20, 4 );
224
+
225
+ add_action( 'network_admin_menu', array( self::$instance, 'admin_menu' ), 20 );
226
+ add_filter( 'network_admin_plugin_action_links', array( self::$instance, 'plugin_action_links' ), 20, 4 );
227
+
228
+ add_filter( 'theme_action_links', array( self::$instance, 'theme_action_links' ), 20, 4 );
229
+ add_filter( 'wp_ajax_wpr_check_changelog', array( self::$instance, 'get_plugin_changelog' ) );
230
+
231
+ }
232
+
233
  /**
234
  * Include required files
235
  *
250
  * @param $hook
251
  *
252
  * @return void
 
253
  */
254
  public function scripts( $hook ) {
255
 
256
+ if ( 'themes.php' === $hook ) {
257
  wp_enqueue_script( 'wp_rollback_themes_script', plugin_dir_url( __FILE__ ) . 'assets/js/themes-wp-rollback.js', array( 'jquery' ), false, true );
258
+ // Localize for i18n
259
  wp_localize_script( 'wp_rollback_themes_script', 'wpr_vars', array(
260
  'ajaxurl' => admin_url(),
261
  'ajax_loader' => admin_url( 'images/spinner.gif' ),
266
  ) );
267
  }
268
 
269
+ if ( ! in_array( $hook, array( 'index_page_wp-rollback', 'dashboard_page_wp-rollback' ) ) ) {
270
  return;
271
  }
272
 
273
  $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
274
 
275
+ // CSS
276
  wp_register_style( 'wp_rollback_css', plugin_dir_url( __FILE__ ) . 'assets/css/wp-rollback.css' );
277
  wp_enqueue_style( 'wp_rollback_css' );
278
 
279
  wp_register_style( 'wp_rollback_modal_css', plugin_dir_url( __FILE__ ) . 'assets/css/magnific-popup.css' );
280
  wp_enqueue_style( 'wp_rollback_modal_css' );
281
 
282
+ // JS
283
  wp_register_script( 'wp_rollback_modal', plugin_dir_url( __FILE__ ) . 'assets/js/jquery.magnific-popup' . $suffix . '.js', array( 'jquery' ) );
284
  wp_enqueue_script( 'wp_rollback_modal' );
285
 
288
 
289
  wp_enqueue_script( 'updates' );
290
 
291
+ // Localize for i18n.
292
  wp_localize_script( 'wp_rollback_script', 'wpr_vars', array(
293
+ 'ajaxurl' => admin_url(),
294
+ 'text_no_changelog_found' => sprintf( __( 'Sorry, we couldn\'t find a changelog entry found for this version. Try checking the <a href="%s" target="_blank">developer log</a> on WP.org.', 'wp-rollback' ), 'https://wordpress.org/plugins/' . $_GET['plugin_slug'] . '/#developers' ),
295
+ 'version_missing' => __( 'Please select a version number to perform a rollback.', 'wp-rollback' ),
296
  ) );
297
 
 
298
  }
299
 
300
  /**
335
  */
336
  public function html() {
337
 
338
+ // Permissions check
339
  if ( ! current_user_can( 'update_plugins' ) ) {
340
  wp_die( __( 'You do not have sufficient permissions to perform rollbacks for this site.', 'wp-rollback' ) );
341
  }
342
 
343
+ // Get the necessary class
344
  include_once( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' );
345
 
346
  $defaults = apply_filters( 'wpr_rollback_html_args', array(
348
  'plugin_file' => '',
349
  'action' => '',
350
  'plugin_version' => '',
351
+ 'plugin' => '',
352
  ) );
353
 
 
354
  $args = wp_parse_args( $_GET, $defaults );
355
 
356
  if ( ! empty( $args['plugin_version'] ) ) {
357
+ // Plugin: rolling back.
358
  check_admin_referer( 'wpr_rollback_nonce' );
359
  include WP_ROLLBACK_PLUGIN_DIR . '/includes/class-rollback-plugin-upgrader.php';
360
  include WP_ROLLBACK_PLUGIN_DIR . '/includes/rollback-action.php';
361
  } elseif ( ! empty( $args['theme_version'] ) ) {
362
+ // Theme: rolling back.
363
  check_admin_referer( 'wpr_rollback_nonce' );
364
  include WP_ROLLBACK_PLUGIN_DIR . '/includes/class-rollback-theme-upgrader.php';
365
  include WP_ROLLBACK_PLUGIN_DIR . '/includes/rollback-action.php';
366
  } else {
367
+ // This is the menu.
368
  check_admin_referer( 'wpr_rollback_nonce' );
369
  include WP_ROLLBACK_PLUGIN_DIR . '/includes/rollback-menu.php';
370
 
373
  }
374
 
375
 
376
+ /**
377
+ * Get Plugin Changelog
378
+ *
379
+ * Uses WP.org API to get a plugin's
380
+ *
381
+ * @return null|string
382
+ */
383
+ public function get_plugin_changelog() {
384
+
385
+ // Need slug to continue.
386
+ if ( ! isset( $_POST['slug'] ) || empty( $_POST['slug'] ) ) {
387
+ return false;
388
+ }
389
+
390
+ $url = 'https://api.wordpress.org/plugins/info/1.0/' . $_POST['slug'];
391
+
392
+ $response = wp_remote_get( $url );
393
+
394
+ // Do we have an error?
395
+ if ( wp_remote_retrieve_response_code( $response ) !== 200 ) {
396
+ return null;
397
+ }
398
+
399
+ $response = maybe_unserialize( wp_remote_retrieve_body( $response ) );
400
+
401
+ // Nope: Return that bad boy
402
+ echo $response->sections['changelog'];
403
+
404
+ wp_die();
405
+
406
+ }
407
+
408
+
409
  /**
410
  * Get Subversion Tags
411
  *
412
+ * cURLs wp.org repo to get the proper tags
413
  *
414
  * @param $type
415
  * @param $slug
416
  *
417
  * @return null|string
418
  */
419
+ public function get_svn_tags( $type, $slug ) {
420
 
421
  $url = $this->plugins_repo . '/' . $this->plugin_slug . '/tags/';
422
 
423
+ // is this a theme svn request?
424
+ if ( 'theme' === $type ) {
425
  $url = $this->themes_repo . '/' . $slug;
426
  }
427
 
428
  $response = wp_remote_get( $url );
429
 
430
+ // Do we have an error?
431
  if ( wp_remote_retrieve_response_code( $response ) !== 200 ) {
432
  return null;
433
  }
434
 
435
+ // Nope: Return that bad boy
436
  return wp_remote_retrieve_body( $response );
437
 
438
  }
440
  /**
441
  * Set SVN Version Data
442
  *
 
 
443
  * @param $html
444
  *
445
  * @return array|bool
446
  */
447
+ public function set_svn_versions_data( $html ) {
448
 
449
  if ( ! $html ) {
450
  return false;
458
  $items = $DOM->getElementsByTagName( 'a' );
459
 
460
  foreach ( $items as $item ) {
461
+ $href = str_replace( '/', '', $item->getAttribute( 'href' ) ); // Remove trailing slash
462
 
463
+ if ( strpos( $href, 'http' ) === false && '..' !== $href ) {
464
  $versions[] = $href;
465
  }
466
  }
485
  return false;
486
  }
487
 
488
+ $versions_html = '<ul class="wpr-version-list">';
489
 
490
  usort( $this->versions, 'version_compare' );
491
 
492
  $this->versions = array_reverse( $this->versions );
493
 
494
+
495
+ // Loop through versions and output in a radio list.
496
  foreach ( $this->versions as $version ) {
497
 
498
+ $versions_html .= '<li class="wpr-version-li">';
499
  $versions_html .= '<label><input type="radio" value="' . esc_attr( $version ) . '" name="' . $type . '_version">' . $version;
500
 
501
+ // Is this the current version?
502
  if ( $version === $this->current_version ) {
503
  $versions_html .= '<span class="current-version">' . __( 'Installed Version', 'wp-rollback' ) . '</span>';
504
  }
505
 
506
  $versions_html .= '</label>';
507
 
508
+ // View changelog link.
509
+ if ( 'plugin' === $type ) {
510
+ $versions_html .= ' <a href="#" class="wpr-changelog-link" data-version="' . $version . '">' . __( 'View Changelog', 'wp-rollback' ) . '</a>';
511
+ }
512
+
513
+ $versions_html .= '</li>';
514
 
515
  }
516
 
517
+ $versions_html .= '</ul>';
518
+
519
+ return apply_filters( 'versions_select_html', $versions_html );
520
  }
521
 
522
  /**
523
  * Set Plugin Slug
524
  *
525
+ * @return array|bool
526
  */
527
  private function set_plugin_slug() {
528
 
543
  wp_die( 'Plugin you\'re referencing does not exist.' );
544
  }
545
 
546
+ // the plugin slug is the base directory name without the path to the main file
 
 
547
  $plugin_slug = explode( '/', plugin_basename( $plugin_file ) );
548
 
549
  $this->plugin_file = apply_filters( 'wpr_plugin_file', $plugin_file );
560
  */
561
  public function admin_menu() {
562
 
563
+ // Only show menu item when necessary (user is interacting with plugin, ie rolling back something)
564
  if ( isset( $_GET['page'] ) && $_GET['page'] == 'wp-rollback' ) {
565
 
566
+ // Add it in a native WP way, like WP updates do... (a dashboard page)
567
  add_dashboard_page( __( 'Rollback', 'wp-rollback' ), __( 'Rollback', 'wp-rollback' ), 'update_plugins', 'wp-rollback', array(
568
  self::$instance,
569
+ 'html',
570
  ) );
571
 
572
  }
605
  */
606
  public function plugin_action_links( $actions, $plugin_file, $plugin_data, $context ) {
607
 
608
+ // Filter for other devs.
 
 
 
609
  $plugin_data = apply_filters( 'wpr_plugin_data', $plugin_data );
610
 
611
+ // If plugin is missing package data do not output Rollback option.
612
  if ( ! isset( $plugin_data['package'] ) || strpos( $plugin_data['package'], 'https://downloads.wordpress.org' ) === false ) {
613
  return $actions;
614
  }
615
 
616
+ // Multisite check.
617
+ if ( is_multisite() && ( ! is_network_admin() && ! is_main_site() ) ) {
618
+ return $actions;
619
+ }
620
+
621
+ // Must have version.
622
+ if ( ! isset( $plugin_data['Version'] ) ) {
623
+ return $actions;
624
  }
625
 
626
+ // Base rollback URL
627
+ $rollback_url = 'index.php?page=wp-rollback&type=plugin&plugin_file=' . $plugin_file;
628
+
629
+ $rollback_url = add_query_arg( apply_filters( 'wpr_plugin_query_args', array(
630
+ 'current_version' => urlencode( $plugin_data['Version'] ),
631
+ 'rollback_name' => urlencode( $plugin_data['Name'] ),
632
+ 'plugin_slug' => urlencode( $plugin_data['slug'] ),
633
+ '_wpnonce' => wp_create_nonce( 'wpr_rollback_nonce' ),
634
+ ) ), $rollback_url );
635
+
636
+ // Final Output
637
  $actions['rollback'] = apply_filters( 'wpr_plugin_markup', '<a href="' . esc_url( $rollback_url ) . '">' . __( 'Rollback', 'wp-rollback' ) . '</a>' );
638
 
639
  return apply_filters( 'wpr_plugin_action_links', $actions );
641
  }
642
 
643
 
644
+ /**
645
+ * Theme Action Links
646
+ *
647
+ * Adds a "rollback" link into the plugins listing page w/ appropriate query strings
648
+ *
649
+ * @param $actions
650
+ * @param $theme WP_Theme
651
+ * @param $context
652
+ *
653
+ * @return array $actions
654
+ */
655
+ public function theme_action_links( $actions, $theme, $context ) {
656
+
657
+ $rollback_themes = get_site_transient( 'rollback_themes' );
658
+ if ( ! is_object( $rollback_themes ) ) {
659
+ $this->wpr_theme_updates_list();
660
+ $rollback_themes = get_site_transient( 'rollback_themes' );
661
+ }
662
+
663
+ $theme_slug = isset( $theme->template ) ? $theme->template : '';
664
+
665
+ // Only WP.org themes.
666
+ if ( empty( $theme_slug ) || ! array_key_exists( $theme_slug, $rollback_themes->response ) ) {
667
+ return $actions;
668
+ }
669
+
670
+ $theme_file = isset( $rollback_themes->response[ $theme_slug ]['package'] ) ? $rollback_themes->response[ $theme_slug ]['package'] : '';
671
+
672
+ // Base rollback URL.
673
+ $rollback_url = 'index.php?page=wp-rollback&type=theme&theme_file=' . $theme_file;
674
+
675
+ // Add in the current version for later reference.
676
+ if ( ! $theme->get( 'Version' ) ) {
677
+ return $actions;
678
+ }
679
+
680
+ $rollback_url = add_query_arg( apply_filters( 'wpr_theme_query_args', array(
681
+ 'theme_file' => urlencode( $theme_slug ),
682
+ 'current_version' => urlencode( $theme->get( 'Version' ) ),
683
+ 'rollback_name' => urlencode( $theme->get( 'Name' ) ),
684
+ '_wpnonce' => wp_create_nonce( 'wpr_rollback_nonce' ),
685
+ ) ), $rollback_url );
686
+
687
+ // Final Output
688
+ $actions['rollback'] = apply_filters( 'wpr_theme_markup', '<a href="' . esc_url( $rollback_url ) . '">' . __( 'Rollback', 'wp-rollback' ) . '</a>' );
689
+
690
+ return apply_filters( 'wpr_theme_action_links', $actions );
691
+
692
+ }
693
+
694
  /**
695
  * Is WordPress Theme?
696
  *
697
  * Queries the WordPress.org API via theme's slug to see if this theme is on WordPress.
698
  *
699
  * @return bool
 
700
  */
701
  public function is_wordpress_theme() {
702
 
703
+ // Multisite check.
704
+ if ( is_multisite() && ( ! is_network_admin() && ! is_main_site() ) ) {
705
+ return false;
706
+ }
707
+
708
  $url = add_query_arg( 'request[slug]', $_POST['theme'], 'https://api.wordpress.org/themes/info/1.1/?action=theme_information' );
709
  $wp_api = wp_remote_get( $url );
710
 
721
  // Die is required to terminate immediately and return a proper response.
722
  wp_die();
723
 
 
 
724
  }
725
 
726
 
748
 
749
  include( ABSPATH . WPINC . '/version.php' ); // include an unmodified $wp_version
750
 
751
+ // Bounce out if improperly called.
752
  if ( defined( 'WP_INSTALLING' ) || ! is_admin() ) {
753
  return false;
754
  }
784
 
785
  $timeout = 3 + (int) ( count( $themes ) / 10 );
786
 
787
+ global $wp_version;
788
+
789
  $options = array(
790
  'timeout' => $timeout,
791
  'body' => array(
792
  'themes' => json_encode( $request ),
793
  ),
794
+ 'user-agent' => 'WordPress/' . $wp_version . '; ' . get_bloginfo( 'url' ),
795
  );
796
 
797
  $url = $http_url = 'http://api.wordpress.org/themes/update-check/1.1/';
817
 
818
  $response = json_decode( wp_remote_retrieve_body( $raw_response ), true );
819
 
820
+ if ( is_array( $response ) && isset( $response['themes'] ) ) {
821
  $new_update->response = $response['themes'];
822
  }
823
 
840
  $rollbacks = array();
841
  $wp_themes = get_site_transient( 'rollback_themes' );
842
 
843
+ // Double check our transient is present.
844
+ if ( empty( $wp_themes ) || ! is_object( $wp_themes ) ) {
845
  $this->wpr_theme_updates_list();
846
  $wp_themes = get_site_transient( 'rollback_themes' );
847
  }
848
 
849
+ // Set $rollback response variable for loop ahead.
850
  if ( is_object( $wp_themes ) ) {
851
  $rollbacks = $wp_themes->response;
852
  }
853
 
854
+ // Loop through themes and provide a 'hasRollback' boolean key for JS.
855
  foreach ( $prepared_themes as $key => $value ) {
856
  $themes[ $key ] = $prepared_themes[ $key ];
857
  $themes[ $key ]['hasRollback'] = isset( $rollbacks[ $key ] );
860
  return $themes;
861
  }
862
 
 
863
  }
864
  }
865
 
876
  * Example: <?php $wp_rollback = WP_Rollback(); ?>
877
  *
878
  * @since 1.0
879
+ * @return WP_Rollback object The one true WP Rollback Instance
880
  */
881
  function WP_Rollback() {
882
  return WP_Rollback::instance();
883
  }
884
 
885
  // Get WP Rollback Running
886
+ WP_Rollback();