first commit

This commit is contained in:
2026-03-05 13:07:40 +01:00
commit 64ba0721ee
25709 changed files with 4691006 additions and 0 deletions

View File

@@ -0,0 +1,484 @@
/* Magnific Popup CSS */
.white-popup {
position: relative;
background: #FFF;
padding: 20px;
width: auto;
max-width: 500px;
margin: 20px auto;
}
.mfp-bg {
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 9998;
overflow: hidden;
position: fixed;
background: #0b0b0b;
opacity: 0.8;
filter: alpha(opacity=80);
}
.mfp-wrap {
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 9999;
position: fixed;
outline: none !important;
-webkit-backface-visibility: hidden;
}
.mfp-container {
text-align: center;
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
padding: 0 8px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.mfp-container:before {
content: '';
display: inline-block;
height: 100%;
vertical-align: middle;
}
.mfp-align-top .mfp-container:before {
display: none;
}
.mfp-content {
position: relative;
display: inline-block;
vertical-align: middle;
margin: 0 auto;
text-align: left;
z-index: 1045;
}
.mfp-inline-holder .mfp-content, .mfp-ajax-holder .mfp-content {
width: 100%;
cursor: auto;
}
.mfp-ajax-cur {
cursor: progress;
}
.mfp-zoom-out-cur, .mfp-zoom-out-cur .mfp-image-holder .mfp-close {
cursor: -moz-zoom-out;
cursor: -webkit-zoom-out;
cursor: zoom-out;
}
.mfp-zoom {
cursor: pointer;
cursor: -webkit-zoom-in;
cursor: -moz-zoom-in;
cursor: zoom-in;
}
.mfp-auto-cursor .mfp-content {
cursor: auto;
}
.mfp-close, .mfp-arrow, .mfp-preloader, .mfp-counter {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
.mfp-loading.mfp-figure {
display: none;
}
.mfp-hide {
display: none !important;
}
.mfp-preloader {
color: #CCC;
position: absolute;
top: 50%;
width: auto;
text-align: center;
margin-top: -0.8em;
left: 8px;
right: 8px;
z-index: 1044;
}
.mfp-preloader a {
color: #CCC;
}
.mfp-preloader a:hover {
color: #FFF;
}
.mfp-s-ready .mfp-preloader {
display: none;
}
.mfp-s-error .mfp-content {
display: none;
}
button.mfp-close, button.mfp-arrow {
overflow: visible;
cursor: pointer;
background: transparent;
border: 0;
-webkit-appearance: none;
display: block;
outline: none;
padding: 0;
z-index: 1046;
-webkit-box-shadow: none;
box-shadow: none;
}
button::-moz-focus-inner {
padding: 0;
border: 0;
}
.mfp-close {
width: 44px;
height: 44px;
line-height: 44px;
position: absolute;
right: 0;
top: 0;
text-decoration: none;
text-align: center;
opacity: 0.65;
filter: alpha(opacity=65);
padding: 0 0 18px 10px;
color: #FFF;
font-style: normal;
font-size: 28px;
font-family: Arial, Baskerville, monospace;
}
.mfp-close:hover, .mfp-close:focus {
opacity: 1;
filter: alpha(opacity=100);
}
.mfp-close:active {
top: 1px;
}
.mfp-close-btn-in .mfp-close {
color: #333;
}
.mfp-image-holder .mfp-close, .mfp-iframe-holder .mfp-close {
color: #FFF;
right: -6px;
text-align: right;
padding-right: 6px;
width: 100%;
}
.mfp-counter {
position: absolute;
top: 0;
right: 0;
color: #CCC;
font-size: 12px;
line-height: 18px;
white-space: nowrap;
}
.mfp-arrow {
position: absolute;
opacity: 0.65;
filter: alpha(opacity=65);
margin: 0;
top: 50%;
margin-top: -55px;
padding: 0;
width: 90px;
height: 110px;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
.mfp-arrow:active {
margin-top: -54px;
}
.mfp-arrow:hover, .mfp-arrow:focus {
opacity: 1;
filter: alpha(opacity=100);
}
.mfp-arrow:before, .mfp-arrow:after, .mfp-arrow .mfp-b, .mfp-arrow .mfp-a {
content: '';
display: block;
width: 0;
height: 0;
position: absolute;
left: 0;
top: 0;
margin-top: 35px;
margin-left: 35px;
border: medium inset transparent;
}
.mfp-arrow:after, .mfp-arrow .mfp-a {
border-top-width: 13px;
border-bottom-width: 13px;
top: 8px;
}
.mfp-arrow:before, .mfp-arrow .mfp-b {
border-top-width: 21px;
border-bottom-width: 21px;
opacity: 0.7;
}
.mfp-arrow-left {
left: 0;
}
.mfp-arrow-left:after, .mfp-arrow-left .mfp-a {
border-right: 17px solid #FFF;
margin-left: 31px;
}
.mfp-arrow-left:before, .mfp-arrow-left .mfp-b {
margin-left: 25px;
border-right: 27px solid #3F3F3F;
}
.mfp-arrow-right {
right: 0;
}
.mfp-arrow-right:after, .mfp-arrow-right .mfp-a {
border-left: 17px solid #FFF;
margin-left: 39px;
}
.mfp-arrow-right:before, .mfp-arrow-right .mfp-b {
border-left: 27px solid #3F3F3F;
}
.mfp-iframe-holder {
padding-top: 40px;
padding-bottom: 40px;
}
.mfp-iframe-holder .mfp-content {
line-height: 0;
width: 100%;
max-width: 900px;
}
.mfp-iframe-holder .mfp-close {
top: -40px;
}
.mfp-iframe-scaler {
width: 100%;
height: 0;
overflow: hidden;
padding-top: 56.25%;
}
.mfp-iframe-scaler iframe {
position: absolute;
display: block;
top: 0;
left: 0;
width: 100%;
height: 100%;
box-shadow: 0 0 8px rgba(0, 0, 0, 0.6);
background: #000;
}
/* Main image in popup */
img.mfp-img {
width: auto;
max-width: 100%;
height: auto;
display: block;
line-height: 0;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
padding: 40px 0 40px;
margin: 0 auto;
}
/* The shadow behind the image */
.mfp-figure {
line-height: 0;
}
.mfp-figure:after {
content: '';
position: absolute;
left: 0;
top: 40px;
bottom: 40px;
display: block;
right: 0;
width: auto;
height: auto;
z-index: -1;
box-shadow: 0 0 8px rgba(0, 0, 0, 0.6);
background: #444;
}
.mfp-figure small {
color: #BDBDBD;
display: block;
font-size: 12px;
line-height: 14px;
}
.mfp-figure figure {
margin: 0;
}
.mfp-bottom-bar {
margin-top: -36px;
position: absolute;
top: 100%;
left: 0;
width: 100%;
cursor: auto;
}
.mfp-title {
text-align: left;
line-height: 18px;
color: #F3F3F3;
word-wrap: break-word;
padding-right: 36px;
}
.mfp-image-holder .mfp-content {
max-width: 100%;
}
.mfp-gallery .mfp-image-holder .mfp-figure {
cursor: pointer;
}
@media screen and (max-width: 800px) and (orientation: landscape), screen and (max-height: 300px) {
/**
* Remove all paddings around the image on small screen
*/
.mfp-img-mobile .mfp-image-holder {
padding-left: 0;
padding-right: 0;
}
.mfp-img-mobile img.mfp-img {
padding: 0;
}
.mfp-img-mobile .mfp-figure:after {
top: 0;
bottom: 0;
}
.mfp-img-mobile .mfp-figure small {
display: inline;
margin-left: 5px;
}
.mfp-img-mobile .mfp-bottom-bar {
background: rgba(0, 0, 0, 0.6);
bottom: 0;
margin: 0;
top: auto;
padding: 3px 5px;
position: fixed;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.mfp-img-mobile .mfp-bottom-bar:empty {
padding: 0;
}
.mfp-img-mobile .mfp-counter {
right: 5px;
top: 3px;
}
.mfp-img-mobile .mfp-close {
top: 0;
right: 0;
width: 35px;
height: 35px;
line-height: 35px;
background: rgba(0, 0, 0, 0.6);
position: fixed;
text-align: center;
padding: 0;
}
}
@media all and (max-width: 900px) {
.mfp-arrow {
-webkit-transform: scale(0.75);
transform: scale(0.75);
}
.mfp-arrow-left {
-webkit-transform-origin: 0;
transform-origin: 0;
}
.mfp-arrow-right {
-webkit-transform-origin: 100%;
transform-origin: 100%;
}
.mfp-container {
padding-left: 6px;
padding-right: 6px;
}
}
.mfp-ie7 .mfp-img {
padding: 0;
}
.mfp-ie7 .mfp-bottom-bar {
width: 600px;
left: 50%;
margin-left: -300px;
margin-top: 5px;
padding-bottom: 5px;
}
.mfp-ie7 .mfp-container {
padding: 0;
}
.mfp-ie7 .mfp-content {
padding-top: 44px;
}
.mfp-ie7 .mfp-close {
top: 0;
right: 0;
padding-top: 0;
}

View File

@@ -0,0 +1,160 @@
/**
* WP Rollback Styles
*/
.wpr-content-wrap {
max-width: 805px;
}
.wpr-content-wrap h1 {
padding: 0 0 15px 0;
}
.wpr-content-wrap h1 img {
width: 40px;
position: relative;
top: 11px;
margin-right: 10px;
}
.wpr-content-wrap p {
font-size: 18px;
margin-top: 10px;
}
.wpr-content-wrap p span {
font-style: italic;
color: #000;
font-weight: 600;
}
.wpr-versions-wrap {
margin: 0 0 30px;
padding: 0 0 0 4px;
}
.wpr-versions-wrap .wpr-error {
max-width: 770px;
}
.wpr-submit-wrap {
border-top: 1px solid #DADADA;
position: fixed;
width: 100%;
display: block;
bottom: 0;
background: #FFF;
margin: 0 0 0 -22px;
padding: 20px 0 20px 20px;
z-index: 9999;
}
.rollback-form .current-version {
text-transform: lowercase;
font-size: 12px;
font-style: italic;
color: #AEAEAE;
margin-left: 14px;
}
.wpr-selected {
font-weight: bold;
}
.wpr-rollback-disabled.magnific-popup.button-primary {
opacity: 0.6;
cursor: default;
}
.button-primary {
margin-right: 5px !important;
}
/* List */
.wpr-version-li {
clear: left;
padding: 8px;
overflow: hidden;
width: 800px;
}
.wpr-version-li label {
float:left;
font-size: 16px;
}
.wpr-version-li label input {
margin-right: 5px;
}
/* Modal */
#wpr-modal-confirm .button-primary {
margin-right: 5px;
}
.wpr-error {
background: #fff;
border-left: 4px solid #FFB800;
-webkit-box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.1);
box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.1);
margin: 20px 0;
padding: 1px 12px;
}
.wpr-error p {
margin: 0.5em 0;
padding: 2px;
}
.wpr-rollback-intro {
font-size: 18px;
margin-top: 0;
}
/* Changelog link */
.wpr-changelog-link {
float: left;
margin: 4px 0 0 12px;
font-size: 11px;
font-style: italic;
opacity: 0;
line-height: 1;
transition: .15s ease-in-out;
-moz-transition: .15s ease-in-out;
-webkit-transition: .15s ease-in-out;
display: inline-block;
outline:none;
box-shadow:none !important;
text-decoration: none;
}
.wpr-changelog-link:hover {
text-decoration: underline;
}
.wpr-version-li:hover .wpr-changelog-link {
opacity: 0.75;
}
/* Hide full changelog */
.wpr-changelog, .wpr-hidden-changelog {
display: none;
}
/* The changelog that appears when loading via AJAX */
.wpr-changelog-entry {
padding: 6px 25px 18px;
background: #FFF;
margin: 20px 0;
max-height: 350px;
overflow-y: auto;
}
.wpr-no-changelog-message {
margin-bottom:0;
}
.wpr-version-list{
width: 800px;
}

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 15.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="89.23px" height="100px" viewBox="0 0 89.23 100" enable-background="new 0 0 89.23 100" xml:space="preserve">
<path fill="#464342" d="M16.196,68.183c-1.398,0.656-1.515,0.518-3.173,1.23c1.898,4.219,4.532,7.676,7.89,10.79
c0.578-0.766,1.33-1.663,1.987-2.611C20.031,74.901,17.825,71.949,16.196,68.183z"/>
<path fill="#464342" d="M44.213,21.495c-18.771,0-34.042,15.27-34.042,34.041c0,3.354,0.573,6.886,1.481,9.949
c0.961-0.427,1.881-0.604,3.26-1.188l-1.306-8.762c-0.054-3.254,0.952-8.649,2.65-12.456l14.6,40
c-1.733-0.843-3.118-1.677-4.625-2.782c-0.73,1.015-0.974,1.19-2.079,2.646c5.1,3.763,11.132,6.162,17.822,6.579
c0-0.84,0-2.406,0-3.466c-2.259-0.155-4.31-0.544-6.409-1.161l9.184-26.683l9.406,25.773c0.063,0.151,0.139,0.289,0.22,0.422
c-2.61,0.918-5.382,1.49-8.26,1.669V86.5c0,1.084,0,1.935,0,3.022c17.887-0.991,32.139-15.855,32.139-33.987
C78.254,36.765,62.982,21.495,44.213,21.495z M48.431,41.185c1.841-0.097,3.503-0.29,3.503-0.29
c1.646-0.196,1.454-2.619-0.195-2.523c0,0-4.958,0.389-8.16,0.389c-3.006,0-8.059-0.389-8.059-0.389
c-1.651-0.096-1.845,2.425-0.194,2.523c0,0,1.561,0.193,3.21,0.29l4.768,13.065l-6.699,20.087L25.46,41.185
c1.844-0.097,3.502-0.29,3.502-0.29c1.648-0.196,1.454-2.619-0.195-2.523c0,0-4.957,0.389-8.157,0.389
c-0.574,0-1.251-0.013-1.971-0.037c5.473-8.309,14.879-13.795,25.573-13.795c7.967,0,15.222,3.047,20.666,8.036
c-0.131-0.008-0.259-0.024-0.395-0.024c-3.007,0-5.141,2.62-5.141,5.432c0,2.523,1.457,4.656,3.007,7.179
c1.163,2.038,2.523,4.656,2.523,8.44c0,2.62-1.008,5.659-2.329,9.895L59.49,74.084L48.431,41.185z M59.6,81.989l9.347-27.027
c1.746-4.366,2.329-7.858,2.329-10.962c0-1.126-0.076-2.172-0.207-3.147c2.389,4.359,3.747,9.362,3.747,14.683
C74.816,66.825,68.698,76.685,59.6,81.989z"/>
<g>
<path fill="#464342" d="M44.055,11.836V1.196L14.095,18.493l29.959,17.296V25.151c16.979,0,30.793,13.812,30.793,30.791
c0,16.28-12.701,29.641-28.713,30.716v13.34c23.36-1.089,42.027-20.431,42.027-44.056C88.162,31.622,68.376,11.836,44.055,11.836z"
/>
<path fill="#464342" d="M17.346,91.01c6.901,5.272,15.396,8.559,24.628,8.99V86.659c-5.979-0.403-11.487-2.528-16.05-5.872
L17.346,91.01z"/>
<path fill="#464342" d="M15.897,68.367L3.361,72.931c2.468,5.887,6.178,11.131,10.842,15.33l8.876-10.566
C23.44,78.153,17.183,71.27,15.897,68.367z"/>
<path fill="#464342" d="M14.477,64.456c-0.597-2.065-0.989-4.216-1.137-6.434H0c0.177,3.813,0.85,7.495,1.938,10.997L14.477,64.456
z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,179 @@
/**
* Theme Specific WP Rollback
*
* @description: Adds a rollback option to themes
* @copyright: http://opensource.org/licenses/gpl-2.0.php GNU Public License
*/
var wpr_vars;
jQuery.noConflict();
(function( $ ) {
/**
* Content Change DOM Event Listener
*
* @see: http://stackoverflow.com/questions/3233991/jquery-watch-div/3234646#3234646
* @param callback
* @returns {*}
*/
jQuery.fn.contentChange = function( callback ) {
var elms = jQuery( this );
elms.each(
function( i ) {
var elm = jQuery( this );
elm.data( 'lastContents', elm.html() );
window.watchContentChange = window.watchContentChange ? window.watchContentChange : [];
window.watchContentChange.push( { 'element': elm, 'callback': callback } );
}
);
return elms;
};
setInterval( function() {
if ( window.watchContentChange ) {
for ( i in window.watchContentChange ) {
if ( window.watchContentChange[ i ].element.data( 'lastContents' ) !== window.watchContentChange[ i ].element.html() ) {
window.watchContentChange[ i ].callback.apply( window.watchContentChange[ i ].element );
window.watchContentChange[ i ].element.data( 'lastContents', window.watchContentChange[ i ].element.html() );
}
}
}
}, 150 );
// On DOM Ready
$( function() {
var themes;
themes = wp.themes = wp.themes || {};
themes.data = typeof _wpThemeSettings !== 'undefined' ? _wpThemeSettings : '';
// Is only one theme active?
if( themes.data.themes.length === 1 ) {
// Show the rollback button.
wpr_theme_rollback(themes.data.themes[0].id);
}
// On clicking a theme template
$( '.theme-overlay' ).contentChange( function( e ) {
// get theme name that was clicked
var clicked_theme = wpr_get_parameter_by_name( 'theme' );
// check that rollback button hasn't been placed
if ( is_rollback_btn_there() ) {
// button is there, bail
return false;
}
// pass off to rollback function
wpr_theme_rollback( clicked_theme );
} );
/**
* Check to see if Rollback button is in place
*
* @returns {boolean}
*/
function is_rollback_btn_there() {
if ( $( '.wpr-theme-rollback' ).length > 0 ) {
return true;
}
return false;
}
/**
* Is Theme WordPress.org?
*
* Rollback only supports WordPress.org themes.
*
* @param theme
*/
function wpr_theme_rollback( theme ) {
var theme_data = wpr_get_theme_data( theme );
// ensure this theme can be rolled back (not premium, etc)
if ( theme_data !== null && typeof theme_data.hasRollback !== 'undefined' && theme_data.hasRollback !== false ) {
var active_theme = $( '.theme-overlay' ).hasClass( 'active' );
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>';
$( '.theme-wrap' ).find( '.theme-actions' ).append( rollback_btn_html );
} else {
// Can't roll back this theme, display the notice.
$( '.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>' );
}
}
/**
* Get Theme Data
*
* @description Loops through the wp.themes.data.themes object, finds a match, and returns the data
* @param theme
* @returns {*}
*/
function wpr_get_theme_data( theme ) {
var theme_data = wp.themes.data.themes;
// Loop through complete theme data to find this current theme's data
for ( var i = 0, len = theme_data.length; i < len; i ++ ) {
if ( theme_data[ i ].id === theme ) {
return theme_data[ i ]; // Return as soon as the object is found
}
}
return null; // The object was not found
}
/**
* JS Ready Query String (Helper)
*
* Kinda dirty but whatever...
*
* @see: http://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript
* @param name
* @returns {string}
*/
function wpr_get_parameter_by_name( name ) {
name = name.replace( /[\[]/, '\\[' ).replace( /[\]]/, '\\]' );
var regex = new RegExp( '[\\?&]' + name + '=([^&#]*)' ),
results = regex.exec( location.search );
return results === null ? '' : decodeURIComponent( results[ 1 ].replace( /\+/g, ' ' ) );
}
/**
* Get Parameter from Focused Theme
*
* @returns {*}
*/
function wpr_get_parameter_from_focused_theme() {
var focused_theme = wp.themes.focusedTheme;
var name = $( focused_theme ).find( '.theme-name' ).attr( 'id' );
if ( typeof name !== 'undefined' ) {
name = name.replace( '-name', '' );
} else {
return false;
}
return name;
}
/**
* Theme Rollback Button Clicked
*
* Send them over to rollback.
*/
$( 'body' ).on( 'click', '.wpr-theme-rollback', function( e ) {
window.location = $( this ).attr( 'href' );
} );
} );
})( jQuery );

View File

@@ -0,0 +1,183 @@
/**
* WP Rollback Scripts
*
* @description:
* @copyright: http://opensource.org/licenses/gpl-2.0.php GNU Public License
*/
var wpr_vars;
jQuery.noConflict();
(function( $ ) {
// On DOM Ready
$( function() {
var form = $( '.rollback-form' );
var form_labels = $( 'label', form.get( 0 ) );
var form_submit_btn = $( '.magnific-popup' );
/**
* On version click
*/
form_labels.on( 'click', function() {
// add a selected class
form_labels.removeClass( 'wpr-selected' );
form_submit_btn.removeClass( 'wpr-rollback-disabled' );
$( this ).addClass( 'wpr-selected' );
// ensure the radio button always gets clicked
$( this ).find( 'input' ).prop( 'checked', true );
} );
/**
* On view changelog clicked.
*/
$( '.wpr-changelog-link' ).on( 'click', function( e ) {
e.preventDefault();
var changelog_container = $( '.wpr-changelog' );
var changelog_placement = $( this ).parent( 'li' );
var version = $( this ).data( 'version' );
// Ensure all change log links are visible.
$('.wpr-changelog-link').removeClass('wpr-hidden-changelog')
// If changelog was already fetched, use that data.
if ( changelog_container.html().length ) {
wpr_append_changelog_entry( changelog_placement, version );
return false;
}
// Get changelog via AJAX.
$.post( ajaxurl, {
'action': 'wpr_check_changelog',
'slug': $( 'input[name="plugin_slug"]' ).val()
}, function( response ) {
// Add changelog to DOM.
$( changelog_container ).append( $.parseHTML( response ) );
// Show changelog entry.
wpr_append_changelog_entry( changelog_placement, version );
}
);
} );
/**
* Show changelog entry.
*
* @param placement
* @param version
*/
function wpr_append_changelog_entry( placement, version ) {
var changelog = $( '.wpr-changelog' );
var changelog_headings = $( changelog ).find( 'h4' );
// Remove old entry.
$( '.wpr-changelog-entry' ).remove();
// Hide this change log link.
$(placement).find('.wpr-changelog-link').addClass('wpr-hidden-changelog');
// Append a new one.
$( placement ).after( '<div class="wpr-changelog-entry"></div>' );
// Loop through changelog headings to get changelog entry.
$( changelog_headings ).each( function( index, value ) {
var raw_val = $( value ).text();
// Match the changelog version heading using regex from: https://github.com/sindresorhus/semver-regex/blob/master/index.js
// 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/;
// from: https://stackoverflow.com/a/27540795/684352
var regex_symver = /(?:(\d+)\.)?(?:(\d+)\.)?(?:(\d+)\.\d+)?(?:(\d+)\.\d+)/;
var found_version_num = raw_val.match( regex_symver );
var found_version_num = $( found_version_num ).get( 0 );
// Match version number.
if ( found_version_num == version ) {
// Assemble entry.
var changelog_heading = $( value ).clone();
var changelog_entry = $( value ).nextUntil( 'h4' ).clone();
// Append changelog entry.
$( '.wpr-changelog-entry' ).append( changelog_heading ).append( changelog_entry );
}
} );
// If no changelog found, show message.
if ( ! $( '.wpr-changelog-entry' ).html().length ) {
$( '.wpr-changelog-entry' ).append( '<p class="wpr-no-changelog-message">' + wpr_vars.text_no_changelog_found + '</p>' );
}
}
/**
* Modal rollback.
*/
form_submit_btn.on( 'click', function() {
var rollback_form_vals = form.serializeArray();
var rollback_version = form.find( 'input[name="plugin_version"]:checked' ).val();
if ( ! rollback_version ) {
rollback_version = form.find( 'input[name="theme_version"]:checked' ).val();
}
var installed_version = form.find( 'input[name="installed_version"]' ).val();
var new_version = form.find( 'input[name="new_version"]' ).val();
var rollback_name = form.find( 'input[name="rollback_name"]' ).val();
// Ensure a version is selected
if ( ! rollback_version ) {
alert( wpr_vars.version_missing );
$.magnificPopup.close(); // just for good measure
} else {
// Passed
$.magnificPopup.open( {
items: {
src: $( '#wpr-modal-confirm' ), // can be a HTML string, jQuery object, or CSS selector
type: 'inline'
},
closeBtnInside: false,
callbacks: {
open: function() {
$( 'span.wpr-plugin-name' ).text( rollback_name );
$( 'span.wpr-installed-version' ).text( installed_version );
$( 'span.wpr-new-version' ).text( rollback_version );
}
}
} );
}
} );
// Modal Close
$( '.wpr-close' ).on( 'click', function( e ) {
e.preventDefault();
$.magnificPopup.close();
} );
// Modal Confirm (GO! GO! GO!)
$( '.wpr-go' ).on( 'click', function( e ) {
// submit form
form.submit();
} );
} );
})( jQuery );

View File

@@ -0,0 +1,97 @@
<?php
/**
* WP Rollback Plugin Upgrader
*
* Class that extends the WP Core Plugin_Upgrader found in core to do rollbacks.
*
* @copyright : http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since : 1.0.0
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Class WP_Rollback_Plugin_Upgrader
*/
class WP_Rollback_Plugin_Upgrader extends Plugin_Upgrader {
/**
* Plugin rollback.
*
* @param $plugin
* @param array $args
*
* @return array|bool|\WP_Error
*/
public function rollback( $plugin, $args = array() ) {
$defaults = array(
'clear_update_cache' => true,
);
$parsed_args = wp_parse_args( $args, $defaults );
$this->init();
$this->upgrade_strings();
// TODO: Add final check to make sure plugin exists
if ( 0 ) {
$this->skin->before();
$this->skin->set_result( false );
$this->skin->error( 'up_to_date' );
$this->skin->after();
return false;
}
$plugin_slug = $this->skin->plugin;
$plugin_version = $this->skin->options['version'];
$download_endpoint = 'https://downloads.wordpress.org/plugin/';
$url = $download_endpoint . $plugin_slug . '.' . $plugin_version . '.zip';
$is_plugin_active = is_plugin_active( $plugin );
add_filter( 'upgrader_pre_install', array( $this, 'deactivate_plugin_before_upgrade' ), 10, 2 );
add_filter( 'upgrader_pre_install', array( $this, 'active_before' ), 10, 2 );
add_filter( 'upgrader_clear_destination', array( $this, 'delete_old_plugin' ), 10, 4 );
add_filter( 'upgrader_post_install', array( $this, 'active_after' ), 10, 2 );
$this->run( array(
'package' => $url,
'destination' => WP_PLUGIN_DIR,
'clear_destination' => true,
'clear_working' => true,
'hook_extra' => array(
'plugin' => $plugin,
'type' => 'plugin',
'action' => 'update',
'bulk' => 'false',
),
) );
remove_action( 'upgrader_process_complete', 'wp_clean_plugins_cache', 9 );
remove_filter( 'upgrader_pre_install', array( $this, 'deactivate_plugin_before_upgrade' ) );
remove_filter( 'upgrader_pre_install', array( $this, 'active_before' ) );
remove_filter( 'upgrader_clear_destination', array( $this, 'delete_old_plugin' ) );
remove_filter( 'upgrader_post_install', array( $this, 'active_after' ) );
if ( ! $this->result || is_wp_error( $this->result ) ) {
return $this->result;
}
if( $is_plugin_active ) {
activate_plugin( $plugin );
}
// Force refresh of plugin update information.
wp_clean_plugins_cache( $parsed_args['clear_update_cache'] );
return true;
}
}

View File

@@ -0,0 +1,88 @@
<?php
/**
* WP Rollback Theme Upgrader
*
* Class that extends the WP Core Theme_Upgrader found in core to do rollbacks.
*
* @copyright : http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since : 1.0.0
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Class WP_Rollback_Theme_Upgrader
*/
class WP_Rollback_Theme_Upgrader extends Theme_Upgrader {
/**
* Theme rollback.
*
* @param $theme
* @param array $args
*
* @return array|bool|\WP_Error
*/
public function rollback( $theme, $args = array() ) {
$defaults = array(
'clear_update_cache' => true,
);
$parsed_args = wp_parse_args( $args, $defaults );
$this->init();
$this->upgrade_strings();
if ( 0 ) {
$this->skin->before();
$this->skin->set_result( false );
$this->skin->error( 'up_to_date' );
$this->skin->after();
return false;
}
$theme_slug = $this->skin->theme;
$theme_version = $this->skin->options['version'];
$download_endpoint = 'https://downloads.wordpress.org/theme/';
$url = $download_endpoint . $theme_slug . '.' . $theme_version . '.zip';
add_filter( 'upgrader_pre_install', array( $this, 'current_before' ), 10, 2 );
add_filter( 'upgrader_post_install', array( $this, 'current_after' ), 10, 2 );
add_filter( 'upgrader_clear_destination', array( $this, 'delete_old_theme' ), 10, 4 );
// '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.
$this->run( array(
'package' => $url,
'destination' => get_theme_root(),
'clear_destination' => true,
'clear_working' => true,
'hook_extra' => array(
'theme' => $theme,
'type' => 'theme',
'action' => 'update',
),
) );
remove_filter( 'upgrader_pre_install', array( $this, 'current_before' ) );
remove_filter( 'upgrader_post_install', array( $this, 'current_after' ) );
remove_filter( 'upgrader_clear_destination', array( $this, 'delete_old_theme' ) );
if ( ! $this->result || is_wp_error( $this->result ) ) {
return $this->result;
}
// Force refresh of theme update information.
wp_clean_themes_cache( $parsed_args['clear_update_cache'] );
return true;
}
}

View File

@@ -0,0 +1,51 @@
<?php
/**
* Rollback Action.
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// Theme rollback.
if ( ! empty( $_GET['theme_file'] ) && file_exists( WP_CONTENT_DIR . '/themes/' . $_GET['theme_file'] ) ) {
// Theme specific vars.
$title = $_GET['rollback_name'];
$nonce = 'upgrade-theme_' . $_GET['theme_file'];
$url = 'index.php?page=wp-rollback&theme_file=' . $args['theme_file'] . 'action=upgrade-theme';
$version = $_GET['theme_version'];
$theme = $_GET['theme_file'];
$upgrader = new WP_Rollback_Theme_Upgrader( new Theme_Upgrader_Skin( compact( 'title', 'nonce', 'url', 'theme', 'version' ) ) );
$result = $upgrader->rollback( $_GET['theme_file'] );
if ( ! is_wp_error( $result ) && $result ) {
do_action( 'wpr_theme_success', $_GET['theme_file'], $_GET['theme_version'] );
} else {
do_action( 'wpr_theme_failure', $result );
}
} elseif ( ! empty( $_GET['plugin_file'] ) && file_exists( WP_PLUGIN_DIR . '/' . $_GET['plugin_file'] ) ) {
// This is a plugin rollback.
$title = $_GET['rollback_name'];
$nonce = 'upgrade-plugin_' . $this->plugin_slug;
$url = 'index.php?page=wp-rollback&plugin_file=' . esc_url( $args['plugin_file'] ) . 'action=upgrade-plugin';
$plugin = $this->plugin_slug;
$version = $args['plugin_version'];
$upgrader = new WP_Rollback_Plugin_Upgrader( new Plugin_Upgrader_Skin( compact( 'title', 'nonce', 'url', 'plugin', 'version' ) ) );
$result = $upgrader->rollback( plugin_basename($this->plugin_file) );
if ( ! is_wp_error( $result ) && $result ) {
do_action( 'wpr_plugin_success', $_GET['plugin_file'], $version );
} else {
do_action( 'wpr_plugin_failure', $result );
}
} else {
_e( 'This rollback request is missing a proper query string. Please contact support.', 'wp-rollback' );
}

View File

@@ -0,0 +1,142 @@
<?php
/**
* Rollback Menu
*
* Provides the rollback screen view with releases.
*/
// Ensure we have our necessary query strings
if ( ( ! isset( $_GET['type'] ) && ! isset( $_GET['theme'] ) ) || ( ! isset( $_GET['type'] ) && ! isset( $_GET['plugin_file'] ) ) ) {
wp_die( __( 'WP Rollback is missing necessary parameters to continue. Please contact support.', 'wp-rollback' ) );
}
$theme_rollback = $_GET['type'] == 'theme' ? true : false;
$plugin_rollback = $_GET['type'] == 'plugin' ? true : false;
$plugins = get_plugins();
?>
<div class="wrap">
<div class="wpr-content-wrap">
<h1>
<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' ); ?>
</h1>
<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>
<div class="wpr-changelog"></div>
</div>
<?php
// A: Plugin rollbacks in first conditional:
if ( isset( $args['plugin_file'] ) && in_array( $args['plugin_file'], array_keys( $plugins ) ) ) {
$versions = WP_Rollback()->versions_select( 'plugin' );
} elseif ( $theme_rollback == true && isset( $_GET['theme_file'] ) ) {
// B: Theme rollback: set up our theme vars
$svn_tags = WP_Rollback()->get_svn_tags( 'theme', $_GET['theme_file'] );
WP_Rollback()->set_svn_versions_data( $svn_tags );
$this->current_version = $_GET['current_version'];
$versions = WP_Rollback()->versions_select( 'theme' );
} else {
// Fallback check
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' ) );
}
?>
<form name="check_for_rollbacks" class="rollback-form" action="<?php echo admin_url( '/index.php' ); ?>">
<?php
// Output Versions
if ( ! empty( $versions ) ) {
?>
<div class="wpr-versions-wrap">
<?php
do_action( 'wpr_pre_versions' );
echo apply_filters( 'wpr_versions_output', $versions );
do_action( 'wpr_post_version' );
?>
</div>
<?php } ?>
<div class="wpr-submit-wrap">
<a href="#wpr-modal-confirm" class="magnific-popup button-primary wpr-rollback-disabled"><?php _e( 'Rollback', 'wp-rollback' ); ?></a>
<input type="button" value="<?php _e( 'Cancel', 'wp-rollback' ); ?>" class="button" onclick="location.href='<?php echo wp_get_referer(); ?>';" />
</div>
<?php do_action( 'wpr_hidden_fields' ); ?>
<input type="hidden" name="page" value="wp-rollback">
<?php
// Important: We need the appropriate file to perform a rollback
if ( $plugin_rollback == true ) {
?>
<input type="hidden" name="plugin_file" value="<?php echo esc_attr( $args['plugin_file'] ); ?>">
<input type="hidden" name="plugin_slug" value="<?php echo esc_attr( $args['plugin_slug'] ); ?>">
<?php } else { ?>
<input type="hidden" name="theme_file" value="<?php echo esc_attr( $_GET['theme_file'] ); ?>">
<?php } ?>
<input type="hidden" name="rollback_name" value="<?php echo esc_attr( $args['rollback_name'] ); ?>">
<input type="hidden" name="installed_version" value="<?php echo esc_attr( $args['current_version'] ); ?>">
<?php wp_nonce_field( 'wpr_rollback_nonce' ); ?>
<div id="wpr-modal-confirm" class="white-popup mfp-hide">
<div class="wpr-modal-inner">
<p class="wpr-rollback-intro"><?php _e( 'Are you sure you want to perform the following rollback?', 'wp-rollback' ); ?></p>
<div class="rollback-details">
<table class="widefat">
<tbody>
<?php do_action( 'wpr_pre_rollback_table' ); ?>
<tr>
<td class="row-title">
<label for="tablecell">
<?php
if ( $plugin_rollback == true ) {
_e( 'Plugin Name:', 'wp-rollback' );
} else {
_e( 'Theme Name:', 'wp-rollback' );
}
?>
</label>
</td>
<td><span class="wpr-plugin-name"></span></td>
</tr>
<tr class="alternate">
<td class="row-title">
<label for="tablecell"><?php _e( 'Installed Version:', 'wp-rollback' ); ?></label>
</td>
<td><span class="wpr-installed-version"></span></td>
</tr>
<tr>
<td class="row-title">
<label for="tablecell"><?php _e( 'New Version:', 'wp-rollback' ); ?></label>
</td>
<td><span class="wpr-new-version"></span></td>
</tr>
<?php do_action( 'wpr_post_rollback_table' ); ?>
</tbody>
</table>
</div>
<div class="wpr-error">
<p><?php
_e( '<strong>Notice:</strong> We strongly recommend you <strong>create a complete backup</strong> 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 resulting from the use of this plugin.', 'wp-rollback' );
?></p>
</div>
<?php do_action( 'wpr_pre_rollback_buttons' ); ?>
<input type="submit" value="<?php _e( 'Rollback', 'wp-rollback' ); ?>" class="button-primary wpr-go" />
<a href="#" class="button wpr-close"><?php _e( 'Cancel', 'wp-rollback' ); ?></a>
<?php do_action( 'wpr_post_rollback_buttons' ); ?>
</div>
</div>
</form>
</div>

View File

@@ -0,0 +1,121 @@
# Copyright (C) 2023 WP Rollback
# This file is distributed under the same license as the WP Rollback package.
msgid ""
msgstr ""
"Project-Id-Version: WP Rollback\n"
"Report-Msgid-Bugs-To: https://github.com/impress-org/wp-rollback\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"PO-Revision-Date: 2023-MO-DA HO:MI+ZONE\n"
"Language-Team: Impress.org <info@impress.org>\n"
"X-Poedit-Basepath: ..\n"
"X-Poedit-SourceCharset: UTF-8\n"
"X-Poedit-KeywordsList: __;_e;_n:1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;esc_attr__;esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c;_n_noop:1,2;_nx_noop:3c,1,2;__ngettext_noop:1,2\n"
"X-Poedit-SearchPath-0: .\n"
"X-Poedit-SearchPathExcluded-0: *.js\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: includes/rollback-action.php:49
msgid "This rollback request is missing a proper query string. Please contact support."
msgstr ""
#: includes/rollback-menu.php:10
msgid "WP Rollback is missing necessary parameters to continue. Please contact support."
msgstr ""
#: includes/rollback-menu.php:23
msgid "WP Rollback"
msgstr ""
#: includes/rollback-menu.php:26
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."
msgstr ""
#: includes/rollback-menu.php:26
msgid "theme"
msgstr ""
#: includes/rollback-menu.php:26
msgid "plugin"
msgstr ""
#: includes/rollback-menu.php:47
msgid "Oh no! We're missing required rollback query strings. Please contact support so we can check this bug out and squash it!"
msgstr ""
#: includes/rollback-menu.php:72, includes/rollback-menu.php:134, wp-rollback.php:266, wp-rollback.php:582, wp-rollback.php:582, wp-rollback.php:657, wp-rollback.php:712
msgid "Rollback"
msgstr ""
#: includes/rollback-menu.php:73, includes/rollback-menu.php:135
msgid "Cancel"
msgstr ""
#: includes/rollback-menu.php:92
msgid "Are you sure you want to perform the following rollback?"
msgstr ""
#: includes/rollback-menu.php:103
msgid "Plugin Name:"
msgstr ""
#: includes/rollback-menu.php:105
msgid "Theme Name:"
msgstr ""
#: includes/rollback-menu.php:114
msgid "Installed Version:"
msgstr ""
#: includes/rollback-menu.php:120
msgid "New Version:"
msgstr ""
#: includes/rollback-menu.php:130
msgid "<strong>Notice:</strong> We strongly recommend you <strong>create a complete backup</strong> 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 resulting from the use of this plugin."
msgstr ""
#: wp-rollback.php:148, wp-rollback.php:160
msgid "Cheatin&#8217; huh?"
msgstr ""
#: wp-rollback.php:267
msgid "No Rollback Available: This is a non-WordPress.org theme."
msgstr ""
#: wp-rollback.php:268
msgid "Loading..."
msgstr ""
#: wp-rollback.php:299
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."
msgstr ""
#: wp-rollback.php:300
msgid "Please select a version number to perform a rollback."
msgstr ""
#: wp-rollback.php:346
msgid "You do not have sufficient permissions to perform rollbacks for this site."
msgstr ""
#: wp-rollback.php:497
msgid "It appears there are no version to select. This is likely due to the %s author not using tags for their versions and only committing new releases to the repository trunk."
msgstr ""
#: wp-rollback.php:517
msgid "Installed Version"
msgstr ""
#: wp-rollback.php:524
msgid "View Changelog"
msgstr ""
#: wp-rollback.php:828
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>."
msgstr ""
#: wp-rollback.php:828
msgid "(WordPress could not establish a secure connection to WordPress.org. Please contact your server administrator.)"
msgstr ""

View File

@@ -0,0 +1,176 @@
=== WP Rollback ===
Contributors: dlocc, drrobotnik, webdevmattcrom, givewp
Tags: rollback, revert, downgrade, version, plugins, themes, version, versions, backup, backups, revision, revisions
Requires at least: 4.8
Donate Link: https://givewp.com/
Tested up to: 6.2
Stable tag: 1.7.3
License: GPLv3
License URI: http://www.gnu.org/licenses/gpl-3.0.html
Rollback (or forward) any WordPress.org plugin or theme like a boss.
== Description ==
Quickly and easily rollback any theme or plugin from WordPress.org to any previous (or newer) version without any of the manual fuss. Works just like the plugin updater, except you're rolling back (or forward) to a specific version. No need for manually downloading and FTPing the files or learning Subversion. This plugin takes care of the trouble for you.
= Rollback WordPress.org Plugins and Themes =
While it's considered best practice to always keep your WordPress plugins and themes updated, we understand there are times you may need to quickly revert to a previous version. This plugin makes that process as easy as a few mouse clicks. Simply select the version of the plugin or theme that you'd like to rollback to, confirm, and in a few moments you'll be using the version requested. No more fumbling to find the version, downloading, unzipping, FTPing, learning Subversion or hair pulling.
= Muy Importante (Very Important): Always Test and Backup =
**Important Disclaimer:** This plugin is not intended to be used without first taking the proper precautions to ensure zero data loss or site downtime. Always be sure you have first tested the rollback on a staging or development site prior to using WP Rollback on a live site.
We provide no (zero) assurances, guarantees, or warranties that the plugin, theme, or WordPress version you are downgrading to will work as you expect. Use this plugin at your own risk.
= Translation Ready =
Do you speak another language? Want to contribute in a meaningful way to WP Rollback? There's no better way than to help us translate the plugin. This plugin is translation ready. Simply header over to the WP Rollback [translation project](https://translate.wordpress.org/projects/wp-plugins/wp-rollback/) that's powered by WordPress.org volunteer translators. There you can contribute to the translation of the plugin into your language.
= Support and Documentation =
We answer all support requests [on the WordPress.org support forum](https://wordpress.org/support/plugin/wp-rollback).
WP Rollback was created to be as intuitive to the natural WordPress experience as possible. There are is no dedicated settings page or options panel. We believe that once you activate WP Rollback, you'll quickly discover exactly how it works without question.
**BUT!!**
We do have documentation on the plugin [Github Wiki](https://github.com/impress-org/wp-rollback/wiki).
== Installation ==
= Minimum Requirements =
* WordPress 4.8 or greater
* PHP version 5.3 or greater
* MySQL version 5.0 or greater
= Automatic installation =
Automatic installation is the easiest option as WordPress handles the file transfers itself and you don't need to leave your web browser. To do an automatic install of WP Rollback, log in to your WordPress dashboard, navigate to the Plugins menu and click Add New.
In the search field type "WP Rollback" and click Search Plugins. Once you have found the plugin you can view details about it such as the point release, rating and description. Most importantly of course, you can install it by simply clicking "Install Now".
= Manual installation =
The manual installation method involves downloading our donation plugin and uploading it to your server via your favorite FTP application. The WordPress codex contains [instructions on how to do this here](http://codex.wordpress.org/Managing_Plugins#Manual_Plugin_Installation).
= Updating =
Automatic updates should work like a charm; as always though, ensure you backup your site just in case.
== Frequently Asked Questions ==
= Is this plugin safe to use? =
Short answer = Yes. Longer answer = It depends on how you use it.
WP Rollback is completely safe because all it does is take publicly available versions of the plugins you already have on your site and install the version that you designate. There is no other kinds of trickery or fancy offsite calls or anything. BUT!!!
Safety largely depends on you. The WordPress website admin. We absolutely do NOT recommend rolling back any plugins or themes on a live site. Test the rollback locally first, have backups, use all the best practice tools available to you. This is intended to make rolling back easier, that's all.
= Why isn't there a rollback button next to X plugin or theme? =
WP Rollback only works with plugins or themes installed from the WordPress Repository. If you don't see the rollback link, then most likely that plugin or theme is not found on WordPress.org. This plugin does not support plugins from GitHub, ThemeForest, or other sources other than the WordPress.org Repo.
= I rolled my [insert plugin name] back to version X.X and now my site is broken. This is your fault. =
Nope. We warned you in **bold** print several times in many places. And our plugin delivered exactly what it said it would do. May the Gods of the internet pity your broken site's soul.
= Where is the complete documentation located? =
The documentation for this plugin is located on our [Github Wiki](https://github.com/impress-org/wp-rollback/wiki). This is where we make regular updates.
= Can this plugin be translated? =
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/impress-org/wp-rollback/).
== Screenshots ==
1. The Rollback link on the Plugins page.
2. The Rollback Versions page for a plugin.
3. The Update plugin screen.
4. The Rollback button on the Theme Modal popup.
5. The Rollback Versions page for a theme.
6. The Rollback modal confirmation popup.
7. The Update theme screen.
== Upgrade Notice ==
This is the first version of this plugin. It is a tool for your convenience. Rollback at your own risk!
== Changelog ==
= 1.7.3 =
* Fix: Resolved an issue with plugin rollbacks not correctly setting a filepath for the plugin being rolled back. Props to WP.org user @itmesteren for the fix.
= 1.7.2 =
* Fix: Ensure that the "Rollback" button displays properly when a WordPress site only has a single theme installed. Thanks [@eldertech](https://wordpress.org/support/users/eldertech/) for your help uncovering this bug.
* Fix: Minor CSS fixes for the Rollback page.
* Tweak: Update the WordPress.org readme.txt file to have better instructions for translating the plugin. We also fixed a few typos.
= 1.7.1 =
* Fix: Prevent PHP notice when rolling back a plugin or theme on PHP 7.4.
= 1.7.0 =
* Tweak: Removed the WP Time Capsule staging button and banner.
= 1.6.0 =
* New: You now have the ability to rollback to the trunk for plugins. This is useful for beta testing releases and more. Thanks to [karpstrucking](https://github.com/karpstrucking) for making this happen. [#45](https://github.com/impress-org/wp-rollback/issues/45)
* New: Add actions "wpr_plugin_success", "wpr_plugin_failure", "wpr_theme_success", and "wpr_theme_failure" for developers.
* New: If a plugin or theme does not have any tagged releases to select from then then an informative notice appears rather than empty space for a better user experience. [#42](https://github.com/impress-org/wp-rollback/issues/42)
* Tweak: Use the WP.org API to retrieve plugin release version information for more reliable results. [#35](https://github.com/impress-org/wp-rollback/issues/35)
= 1.5.1 =
* Tweak: Added additional information about the importance of Staging and Backups and links to our preferred plugin.
= 1.5 =
* New: You can now view plugin changelogs within the rollback screen. [#7](https://github.com/impress-org/wp-rollback/issues/7)
* New: Added support for WordPress Multisite rollbacks for themes and plugins. [#22](https://github.com/impress-org/wp-rollback/issues/22)
* 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/impress-org/wp-rollback/issues/23)
* New: Updated the WP.org plugin header graphic. [#37](https://github.com/impress-org/wp-rollback/issues/37)
= 1.4 =
* New: Updated plugin's text domain to the plugin's slug of 'wp-rollback' to support WordPress' GlotPress translations. [#28](https://github.com/impress-org/wp-rollback/issues/28)
* New: Gulp automated POT file generation and text domain checker. [#28](https://github.com/impress-org/wp-rollback/issues/28)
* 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/impress-org/wp-rollback/issues/27)
= 1.3 =
* Tested compatibility with WordPress 4.4 and verified as working; bumped up compatibility
* Fix: Trying to get property of non-object warning. [#20](https://github.com/impress-org/wp-rollback/issues/20)
* Improvement: Better version sorting now using usort & version_compare. [#16](https://github.com/impress-org/wp-rollback/issues/16)
= 1.2.4 =
* New: Portuguese translations added.
* 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)
= 1.2.3 =
* Fixed: XSS hardening. Thanks @secupress
* Fixed: CSRF patch regarding missing nonces. Thanks @secupress
* Improvement: escape all of the things.
= 1.2.2 =
* New: Russian translations from @Flector - thanks!
* 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)
= 1.2.1 =
* Fix: Rollback link appears on non wp.org plugins - thanks @scottopolis. [#14](https://github.com/impress-org/wp-rollback/issues/14)
* Removed unnecessary WP_ROLLBACK_VERSION constant.
= 1.2 =
* New: Swedish translation files - Thanks @WPDailyThemes.
= 1.1 =
* Fixed "Cancel" button which was falsely submitting the form.
= 1.0 =
* Initial plugin release. Yippee!
* Adds "Rollback" link to all plugins from the WordPress repo on the plugin screen.
* Adds "Rollback" link to all themes from the WordPress repo inside the modal details screen.
* The "Rollback" page allows you to choose which version you want to rollback to.

View File

@@ -0,0 +1,909 @@
<?php
/**
* Plugin Name: WP Rollback
* Plugin URI: https://givewp.com/
* Description: Rollback (or forward) any WordPress.org plugin or theme like a boss.
* Author: GiveWP.com
* Author URI: https://givewp.com/
* Version: 1.7.3
* Text Domain: wp-rollback
* Domain Path: /languages
*
* WP Rollback is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* WP Rollback is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with WP Rollback. If not, see <http://www.gnu.org/licenses/>.
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Main WP Rollback Class
*
* @since 1.0
*/
if ( ! class_exists( 'WP_Rollback' ) ) :
/**
* Class WP_Rollback
*/
final class WP_Rollback {
/**
* WP_Rollback instance
*
* @var WP_Rollback The one and only
* @since 1.0
*/
private static $instance;
/**
* WP_Rollback Settings Object
*
* @var object
* @since 1.0
*/
public $wpr_settings;
/**
* Plugins API url.
*
* @var string
*/
public $plugins_api = 'https://api.wordpress.org/plugins';
/**
* Themes repo url.
*
* @var string
*/
public $themes_repo = 'https://themes.svn.wordpress.org';
/**
* Plugin file.
*
* @var string
*/
public $plugin_file;
/**
* Plugin slug.
*
* @var string
*/
public $plugin_slug;
/**
* Versions.
*
* @var array
*/
public $versions = array();
/**
* Current version.
*
* @var string
*/
public $current_version;
/**
* Main WP_Rollback Instance
*
* Insures that only one instance of WP Rollback exists in memory at any one
* time. Also prevents needing to define globals all over the place.
*
* @since 1.0
* @static
* @staticvar array $instance
* @uses WP_Rollback::setup_constants() Setup the constants needed
* @uses WP_Rollback::includes() Include the required files
* @uses WP_Rollback::load_textdomain() load the language files
* @see WP_Rollback()
* @return WP_Rollback
*/
public static function instance() {
if ( ! isset( self::$instance ) && ! ( self::$instance instanceof WP_Rollback ) && is_admin() ) {
self::$instance = new WP_Rollback();
self::$instance->setup_constants();
// Only setup plugin rollback on specific page
if ( isset( $_GET['plugin_file'] ) && $_GET['page'] == 'wp-rollback' ) {
self::$instance->setup_plugin_vars();
}
self::$instance->hooks();
self::$instance->includes();
}
return self::$instance;
}
/**
* Throw error on object clone
*
* The whole idea of the singleton design pattern is that there is a single
* object therefore, we don't want the object to be cloned.
*
* @since 1.0
* @access protected
* @return void
*/
public function __clone() {
// Cloning instances of the class is forbidden
_doing_it_wrong( __FUNCTION__, __( 'Cheatin&#8217; huh?', 'wp-rollback' ), '1.0' );
}
/**
* Disable unserializing of the class
*
* @since 1.0
* @access protected
* @return void
*/
public function __wakeup() {
// Unserializing instances of the class is forbidden
_doing_it_wrong( __FUNCTION__, __( 'Cheatin&#8217; huh?', 'wp-rollback' ), '1.0' );
}
/**
* Setup plugin constants
*
* @access private
* @since 1.0
* @return void
*/
private function setup_constants() {
// Plugin Folder Path
if ( ! defined( 'WP_ROLLBACK_PLUGIN_DIR' ) ) {
define( 'WP_ROLLBACK_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
}
// Plugin Folder URL
if ( ! defined( 'WP_ROLLBACK_PLUGIN_URL' ) ) {
define( 'WP_ROLLBACK_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
}
// Plugin Root File
if ( ! defined( 'WP_ROLLBACK_PLUGIN_FILE' ) ) {
define( 'WP_ROLLBACK_PLUGIN_FILE', __FILE__ );
}
}
/**
* Setup Variables
*
* @access private
* @description:
*/
private function setup_plugin_vars() {
$this->set_plugin_slug();
$svn_tags = $this->get_svn_tags( 'plugin', $this->plugin_slug );
$this->set_svn_versions_data( $svn_tags );
}
/**
* Plugin hooks.
*
* @access private
* @since 1.5
* @return void
*/
private function hooks() {
// i18n
add_action( 'plugins_loaded', array( self::$instance, 'load_textdomain' ) );
// Admin
add_action( 'admin_enqueue_scripts', array( self::$instance, 'scripts' ) );
add_action( 'admin_menu', array( self::$instance, 'admin_menu' ), 20 );
add_action(
'pre_current_active_plugins', array(
self::$instance,
'pre_current_active_plugins',
), 20, 1
);
add_action( 'wp_ajax_is_wordpress_theme', array( self::$instance, 'is_wordpress_theme' ) );
add_action( 'set_site_transient_update_themes', array( self::$instance, 'wpr_theme_updates_list' ) );
add_filter( 'wp_prepare_themes_for_js', array( self::$instance, 'wpr_prepare_themes_js' ) );
add_filter( 'plugin_action_links', array( self::$instance, 'plugin_action_links' ), 20, 4 );
add_action( 'network_admin_menu', array( self::$instance, 'admin_menu' ), 20 );
add_filter( 'network_admin_plugin_action_links', array( self::$instance, 'plugin_action_links' ), 20, 4 );
add_filter( 'theme_action_links', array( self::$instance, 'theme_action_links' ), 20, 4 );
add_filter( 'wp_ajax_wpr_check_changelog', array( self::$instance, 'get_plugin_changelog' ) );
}
/**
* Include required files
*
* @access private
* @since 1.0
* @return void
*/
private function includes() {
}
/**
* Enqueue Admin Scripts
*
* @access private
* @since 1.0
*
* @param $hook
*
* @return void
*/
public function scripts( $hook ) {
if ( 'themes.php' === $hook ) {
wp_enqueue_script( 'wp_rollback_themes_script', plugin_dir_url( __FILE__ ) . 'assets/js/themes-wp-rollback.js', array( 'jquery' ), false, true );
// Localize for i18n
wp_localize_script(
'wp_rollback_themes_script', 'wpr_vars', array(
'ajaxurl' => admin_url(),
'ajax_loader' => admin_url( 'images/spinner.gif' ),
'nonce' => wp_create_nonce( 'wpr_rollback_nonce' ),
'text_rollback_label' => __( 'Rollback', 'wp-rollback' ),
'text_not_rollbackable' => __( 'No Rollback Available: This is a non-WordPress.org theme.', 'wp-rollback' ),
'text_loading_rollback' => __( 'Loading...', 'wp-rollback' ),
)
);
}
if ( ! in_array( $hook, array( 'index_page_wp-rollback', 'dashboard_page_wp-rollback' ) ) ) {
return;
}
$suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
// CSS
wp_register_style( 'wp_rollback_css', plugin_dir_url( __FILE__ ) . 'assets/css/wp-rollback.css' );
wp_enqueue_style( 'wp_rollback_css' );
wp_register_style( 'wp_rollback_modal_css', plugin_dir_url( __FILE__ ) . 'assets/css/magnific-popup.css' );
wp_enqueue_style( 'wp_rollback_modal_css' );
// JS
wp_register_script( 'wp_rollback_modal', plugin_dir_url( __FILE__ ) . 'assets/js/jquery.magnific-popup' . $suffix . '.js', array( 'jquery' ) );
wp_enqueue_script( 'wp_rollback_modal' );
wp_register_script( 'wp_rollback_script', plugin_dir_url( __FILE__ ) . 'assets/js/wp-rollback.js', array( 'jquery' ) );
wp_enqueue_script( 'wp_rollback_script' );
wp_enqueue_script( 'updates' );
// Localize for i18n.
wp_localize_script(
'wp_rollback_script', 'wpr_vars', array(
'ajaxurl' => admin_url(),
'text_no_changelog_found' => isset( $_GET['plugin_slug'] ) ? 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' ) : '',
'version_missing' => __( 'Please select a version number to perform a rollback.', 'wp-rollback' ),
)
);
}
/**
* Loads the plugin language files
*
* @access public
* @since 1.0
* @return void
*/
public function load_textdomain() {
// Set filter for plugin's languages directory
$wpr_lang_dir = dirname( plugin_basename( WP_ROLLBACK_PLUGIN_FILE ) ) . '/languages/';
$wpr_lang_dir = apply_filters( 'wpr_languages_directory', $wpr_lang_dir );
// Traditional WordPress plugin locale filter
$locale = apply_filters( 'plugin_locale', get_locale(), 'wp-rollback' );
$mofile = sprintf( '%1$s-%2$s.mo', 'wp-rollback', $locale );
// Setup paths to current locale file
$mofile_local = $wpr_lang_dir . $mofile;
$mofile_global = WP_LANG_DIR . '/wp-rollback/' . $mofile;
if ( file_exists( $mofile_global ) ) {
// Look in global /wp-content/languages/wpr folder
load_textdomain( 'wp-rollback', $mofile_global );
} elseif ( file_exists( $mofile_local ) ) {
// Look in local /wp-content/plugins/wpr/languages/ folder
load_textdomain( 'wp-rollback', $mofile_local );
} else {
// Load the default language files
load_plugin_textdomain( 'wp-rollback', false, $wpr_lang_dir );
}
}
/**
* HTML
*/
public function html() {
// Permissions check
if ( ! current_user_can( 'update_plugins' ) ) {
wp_die( __( 'You do not have sufficient permissions to perform rollbacks for this site.', 'wp-rollback' ) );
}
// Get the necessary class
include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
$defaults = apply_filters(
'wpr_rollback_html_args', array(
'page' => 'wp-rollback',
'plugin_file' => '',
'action' => '',
'plugin_version' => '',
'plugin' => '',
)
);
$args = wp_parse_args( $_GET, $defaults );
if ( ! empty( $args['plugin_version'] ) ) {
// Plugin: rolling back.
check_admin_referer( 'wpr_rollback_nonce' );
include WP_ROLLBACK_PLUGIN_DIR . '/includes/class-rollback-plugin-upgrader.php';
include WP_ROLLBACK_PLUGIN_DIR . '/includes/rollback-action.php';
} elseif ( ! empty( $args['theme_version'] ) ) {
// Theme: rolling back.
check_admin_referer( 'wpr_rollback_nonce' );
include WP_ROLLBACK_PLUGIN_DIR . '/includes/class-rollback-theme-upgrader.php';
include WP_ROLLBACK_PLUGIN_DIR . '/includes/rollback-action.php';
} else {
// This is the menu.
check_admin_referer( 'wpr_rollback_nonce' );
include WP_ROLLBACK_PLUGIN_DIR . '/includes/rollback-menu.php';
}
}
/**
* Get Plugin Changelog
*
* Uses WP.org API to get a plugin's
*
* @return null|string
*/
public function get_plugin_changelog() {
// Need slug to continue.
if ( ! isset( $_POST['slug'] ) || empty( $_POST['slug'] ) ) {
return false;
}
$url = 'https://api.wordpress.org/plugins/info/1.0/' . $_POST['slug'];
$response = wp_remote_get( $url );
// Do we have an error?
if ( wp_remote_retrieve_response_code( $response ) !== 200 ) {
return null;
}
$response = maybe_unserialize( wp_remote_retrieve_body( $response ) );
// Nope: Return that bad boy
echo $response->sections['changelog'];
wp_die();
}
/**
* Get Subversion Tags
*
* cURLs wp.org repo to get the proper tags
*
* @param $type
* @param $slug
*
* @return null|string
*/
public function get_svn_tags( $type, $slug ) {
if ( 'plugin' === $type ) {
$url = $this->plugins_api . '/info/1.0/' . $this->plugin_slug . '.json';
} elseif ( 'theme' === $type ) {
$url = $this->themes_repo . '/' . $slug;
}
$response = wp_remote_get( $url );
// Do we have an error?
if ( wp_remote_retrieve_response_code( $response ) !== 200 ) {
return null;
}
// Nope: Return that bad boy
return wp_remote_retrieve_body( $response );
}
/**
* Set SVN Version Data
*
* @param $html
*
* @return array|bool
*/
public function set_svn_versions_data( $html ) {
if ( ! $html ) {
return false;
}
if ( ( $json = json_decode( $html ) ) && ( $html != $json ) ) {
$versions = array_keys( (array) $json->versions );
} else {
$DOM = new DOMDocument();
$DOM->loadHTML( $html );
$versions = array();
$items = $DOM->getElementsByTagName( 'a' );
foreach ( $items as $item ) {
$href = str_replace( '/', '', $item->getAttribute( 'href' ) ); // Remove trailing slash
if ( strpos( $href, 'http' ) === false && '..' !== $href ) {
$versions[] = $href;
}
}
}
$this->versions = array_reverse( $versions );
return $versions;
}
/**
* Versions Select
*
* Outputs the version radio buttons to select a rollback; types = 'plugin' or 'theme'.
*
* @param $type
*
* @return bool|string
*/
public function versions_select( $type ) {
if ( empty( $this->versions ) ) {
$versions_html = '<div class="wpr-error"><p>' . sprintf( __( 'It appears there are no version to select. This is likely due to the %s author not using tags for their versions and only committing new releases to the repository trunk.', 'wp-rollback' ), $type ) . '</p></div>';
return apply_filters( 'versions_failure_html', $versions_html );
}
$versions_html = '<ul class="wpr-version-list">';
usort( $this->versions, 'version_compare' );
$this->versions = array_reverse( $this->versions );
// Loop through versions and output in a radio list.
foreach ( $this->versions as $version ) {
$versions_html .= '<li class="wpr-version-li">';
$versions_html .= '<label><input type="radio" value="' . esc_attr( $version ) . '" name="' . $type . '_version">' . $version;
// Is this the current version?
if ( $version === $this->current_version ) {
$versions_html .= '<span class="current-version">' . __( 'Installed Version', 'wp-rollback' ) . '</span>';
}
$versions_html .= '</label>';
// View changelog link.
if ( 'plugin' === $type ) {
$versions_html .= ' <a href="#" class="wpr-changelog-link" data-version="' . $version . '">' . __( 'View Changelog', 'wp-rollback' ) . '</a>';
}
$versions_html .= '</li>';
}
$versions_html .= '</ul>';
return apply_filters( 'versions_select_html', $versions_html );
}
/**
* Set Plugin Slug
*
* @return array|bool
*/
private function set_plugin_slug() {
if ( ! isset( $_GET['plugin_file'] ) ) {
return false;
}
if ( isset( $_GET['current_version'] ) ) {
$curr_version = explode( ' ', $_GET['current_version'] );
$this->current_version = apply_filters( 'wpr_current_version', $curr_version[0] );
}
include_once ABSPATH . 'wp-admin/includes/plugin.php';
$plugin_file = WP_PLUGIN_DIR . '/' . $_GET['plugin_file'];
if ( ! file_exists( $plugin_file ) ) {
wp_die( 'Plugin you\'re referencing does not exist.' );
}
// the plugin slug is the base directory name without the path to the main file
$plugin_slug = explode( '/', plugin_basename( $plugin_file ) );
$this->plugin_file = apply_filters( 'wpr_plugin_file', $plugin_file );
$this->plugin_slug = apply_filters( 'wpr_plugin_slug', $plugin_slug[0] );
return $plugin_slug;
}
/**
* Admin Menu
*
* Adds a 'hidden' menu item that is activated when the user elects to rollback
*/
public function admin_menu() {
// Only show menu item when necessary (user is interacting with plugin, ie rolling back something)
if ( isset( $_GET['page'] ) && $_GET['page'] == 'wp-rollback' ) {
// Add it in a native WP way, like WP updates do... (a dashboard page)
add_dashboard_page(
__( 'Rollback', 'wp-rollback' ), __( 'Rollback', 'wp-rollback' ), 'update_plugins', 'wp-rollback', array(
self::$instance,
'html',
)
);
}
}
/**
* Pre-Current Active Plugins
*
* @param $plugins
*
* @return mixed
*/
public function pre_current_active_plugins( $plugins ) {
$updated = $plugins;
foreach ( $updated as $key => $value ) {
$updated[ $key ] = $value;
$updated[ $key ]['rollback'] = true;
}
return $updated;
}
/**
* Plugin Action Links
*
* Adds a "rollback" link into the plugins listing page w/ appropriate query strings
*
* @param $actions
* @param $plugin_file
* @param $plugin_data
* @param $context
*
* @return array $actions
*/
public function plugin_action_links( $actions, $plugin_file, $plugin_data, $context ) {
// Filter for other devs.
$plugin_data = apply_filters( 'wpr_plugin_data', $plugin_data );
// If plugin is missing package data do not output Rollback option.
if ( ! isset( $plugin_data['package'] ) || strpos( $plugin_data['package'], 'https://downloads.wordpress.org' ) === false ) {
return $actions;
}
// Multisite check.
if ( is_multisite() && ( ! is_network_admin() && ! is_main_site() ) ) {
return $actions;
}
// Must have version.
if ( ! isset( $plugin_data['Version'] ) ) {
return $actions;
}
// Base rollback URL
$rollback_url = 'index.php?page=wp-rollback&type=plugin&plugin_file=' . $plugin_file;
$rollback_url = add_query_arg(
apply_filters(
'wpr_plugin_query_args', array(
'current_version' => urlencode( $plugin_data['Version'] ),
'rollback_name' => urlencode( $plugin_data['Name'] ),
'plugin_slug' => urlencode( $plugin_data['slug'] ),
'_wpnonce' => wp_create_nonce( 'wpr_rollback_nonce' ),
)
), $rollback_url
);
// Final Output
$actions['rollback'] = apply_filters( 'wpr_plugin_markup', '<a href="' . esc_url( $rollback_url ) . '">' . __( 'Rollback', 'wp-rollback' ) . '</a>' );
return apply_filters( 'wpr_plugin_action_links', $actions );
}
/**
* Theme Action Links
*
* Adds a "rollback" link into the plugins listing page w/ appropriate query strings
*
* @param $actions
* @param $theme WP_Theme
* @param $context
*
* @return array $actions
*/
public function theme_action_links( $actions, $theme, $context ) {
$rollback_themes = get_site_transient( 'rollback_themes' );
if ( ! is_object( $rollback_themes ) ) {
$this->wpr_theme_updates_list();
$rollback_themes = get_site_transient( 'rollback_themes' );
}
$theme_slug = isset( $theme->template ) ? $theme->template : '';
// Only WP.org themes.
if ( empty( $theme_slug ) || ! array_key_exists( $theme_slug, $rollback_themes->response ) ) {
return $actions;
}
$theme_file = isset( $rollback_themes->response[ $theme_slug ]['package'] ) ? $rollback_themes->response[ $theme_slug ]['package'] : '';
// Base rollback URL.
$rollback_url = 'index.php?page=wp-rollback&type=theme&theme_file=' . $theme_file;
// Add in the current version for later reference.
if ( ! $theme->get( 'Version' ) ) {
return $actions;
}
$rollback_url = add_query_arg(
apply_filters(
'wpr_theme_query_args', array(
'theme_file' => urlencode( $theme_slug ),
'current_version' => urlencode( $theme->get( 'Version' ) ),
'rollback_name' => urlencode( $theme->get( 'Name' ) ),
'_wpnonce' => wp_create_nonce( 'wpr_rollback_nonce' ),
)
), $rollback_url
);
// Final Output
$actions['rollback'] = apply_filters( 'wpr_theme_markup', '<a href="' . esc_url( $rollback_url ) . '">' . __( 'Rollback', 'wp-rollback' ) . '</a>' );
return apply_filters( 'wpr_theme_action_links', $actions );
}
/**
* Is WordPress Theme?
*
* Queries the WordPress.org API via theme's slug to see if this theme is on WordPress.
*
* @return bool
*/
public function is_wordpress_theme() {
// Multisite check.
if ( is_multisite() && ( ! is_network_admin() && ! is_main_site() ) ) {
return false;
}
$url = add_query_arg( 'request[slug]', $_POST['theme'], 'https://api.wordpress.org/themes/info/1.1/?action=theme_information' );
$wp_api = wp_remote_get( $url );
if ( ! is_wp_error( $wp_api ) ) {
if ( isset( $wp_api['body'] ) && strlen( $wp_api['body'] ) > 0 && $wp_api['body'] !== 'false' ) {
echo 'wp';
} else {
echo 'non-wp';
}
} else {
echo 'error';
}
// Die is required to terminate immediately and return a proper response.
wp_die();
}
/**
* Plugin Row Meta.
*
* @param $plugin_meta
* @param $plugin_file
* @param $plugin_data
* @param $status
*
* @return mixed
*/
public function plugin_row_meta( $plugin_meta, $plugin_file, $plugin_data, $status ) {
return $plugin_meta;
}
/**
* Updates theme list.
*
* @return bool
*/
function wpr_theme_updates_list() {
include ABSPATH . WPINC . '/version.php'; // include an unmodified $wp_version
// Bounce out if improperly called.
if ( defined( 'WP_INSTALLING' ) || ! is_admin() ) {
return false;
}
$expiration = 12 * HOUR_IN_SECONDS;
$installed_themes = wp_get_themes();
$last_update = get_site_transient( 'update_themes' );
if ( ! is_object( $last_update ) ) {
set_site_transient( 'rollback_themes', time(), $expiration );
}
$themes = $checked = $request = array();
// Put slug of current theme into request.
$request['active'] = get_option( 'stylesheet' );
foreach ( $installed_themes as $theme ) {
$checked[ $theme->get_stylesheet() ] = $theme->get( 'Version' );
$themes[ $theme->get_stylesheet() ] = array(
'Name' => $theme->get( 'Name' ),
'Title' => $theme->get( 'Name' ),
'Version' => '0.0.0.0.0.0',
'Author' => $theme->get( 'Author' ),
'Author URI' => $theme->get( 'AuthorURI' ),
'Template' => $theme->get_template(),
'Stylesheet' => $theme->get_stylesheet(),
);
}
$request['themes'] = $themes;
$timeout = 3 + (int) ( count( $themes ) / 10 );
global $wp_version;
$options = array(
'timeout' => $timeout,
'body' => array(
'themes' => json_encode( $request ),
),
'user-agent' => 'WordPress/' . $wp_version . '; ' . get_bloginfo( 'url' ),
);
$url = $http_url = 'http://api.wordpress.org/themes/update-check/1.1/';
if ( $ssl = wp_http_supports( array( 'ssl' ) ) ) {
$url = set_url_scheme( $url, 'https' );
}
$raw_response = wp_remote_post( $url, $options );
if ( $ssl && is_wp_error( $raw_response ) ) {
trigger_error( __( '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>.', 'wp-rollback' ) . ' ' . __( '(WordPress could not establish a secure connection to WordPress.org. Please contact your server administrator.)', 'wp-rollback' ), headers_sent() || WP_DEBUG ? E_USER_WARNING : E_USER_NOTICE );
$raw_response = wp_remote_post( $http_url, $options );
}
set_site_transient( 'rollback_themes', time(), $expiration );
if ( is_wp_error( $raw_response ) || 200 != wp_remote_retrieve_response_code( $raw_response ) ) {
return false;
}
$new_update = new stdClass();
$new_update->last_checked = time();
$new_update->checked = $checked;
$response = json_decode( wp_remote_retrieve_body( $raw_response ), true );
if ( is_array( $response ) && isset( $response['themes'] ) ) {
$new_update->response = $response['themes'];
}
set_site_transient( 'rollback_themes', $new_update );
return true;
}
/**
* Prepare Themes JS.
*
* @param $prepared_themes
*
* @return array
*/
function wpr_prepare_themes_js( $prepared_themes ) {
$themes = array();
$rollbacks = array();
$wp_themes = get_site_transient( 'rollback_themes' );
// Double-check our transient is present.
if ( empty( $wp_themes ) || ! is_object( $wp_themes ) ) {
$this->wpr_theme_updates_list();
$wp_themes = get_site_transient( 'rollback_themes' );
}
// Set $rollback response variable for loop ahead.
if ( is_object( $wp_themes ) ) {
$rollbacks = $wp_themes->response;
}
// Loop through themes and provide a 'hasRollback' boolean key for JS.
foreach ( $prepared_themes as $key => $value ) {
$themes[ $key ] = $prepared_themes[ $key ];
$themes[ $key ]['hasRollback'] = isset( $rollbacks[ $key ] );
}
return $themes;
}
}
endif; // End if class_exists check
/**
* The main function responsible for returning the one true WP Rollback
* Instance to functions everywhere.
*
* Use this function like you would a global variable, except without needing
* to declare the global.
*
* Example: <?php $wp_rollback = WP_Rollback(); ?>
*
* @since 1.0
* @return WP_Rollback object The one true WP Rollback Instance
*/
function WP_Rollback() {
return WP_Rollback::instance();
}
// Get WP Rollback Running
WP_Rollback();