feat: add S3-compatible storage provider (MinIO, Ceph, R2, etc.)

Adds a new 'S3-Compatible Storage' provider that works with any
S3-API-compatible object storage service, including MinIO, Ceph,
Cloudflare R2, Backblaze B2, and others.

Changes:
- New provider class: classes/providers/storage/s3-compatible-provider.php
  - Provider key: s3compatible
  - Reads user-configured endpoint URL from settings
  - Uses path-style URL access (required by most S3-compatible services)
  - Supports credentials via AS3CF_S3COMPAT_ACCESS_KEY_ID /
    AS3CF_S3COMPAT_SECRET_ACCESS_KEY wp-config.php constants
  - Disables AWS-specific features (Block Public Access, Object Ownership)
- New provider SVG icons (s3compatible.svg, -link.svg, -round.svg)
- Registered provider in main plugin class with endpoint setting support
- Updated StorageProviderSubPage to show endpoint URL input for S3-compatible
- Built pro settings bundle with rollup (Svelte 4.2.19)
- Added package.json and updated rollup.config.mjs for pro-only builds
This commit is contained in:
2026-03-03 12:30:18 +01:00
commit 3248cbb029
2086 changed files with 359427 additions and 0 deletions

View File

@@ -0,0 +1,309 @@
(function( $ ) {
/**
* The list table used on the current page.
* plugins.php : .wp-list-table.plugins
* update-core.php : #update-plugins-table
* @type {Object}
*/
const $listTable = $( '#update-plugins-table,.wp-list-table.plugins' );
/**
* Class representing a single plugin in the context of a list table of plugin updates.
*
* @param {object} data
* @param {Plugin|undefined} parent
* @constructor
*/
const Plugin = function( data, parent ) {
/**
* Data provided by the server.
* @type {Object}
*/
this.data = data;
/**
* Parent Plugin instance, if representing an add-on.
* @type {Plugin|undefined}
*/
this.parent = parent;
/**
* Object for the checkbox allowing the plugin to be submitted for update.
* @type {Object} jQuery
*/
this.$check = $listTable.find( '.check-column [value="' + data.basename + '"]' );
/**
* Object for the primary list table row for this plugin.
* @type {Object} jQuery
*/
this.$row = this.$check.closest( 'tr' );
/**
* Object for the container which holds update notices for this plugin.
* This container represents the main difference between the two list tables.
*
* This property is only initialized for plugins with license errors.
*
* @type {Object} jQuery
*/
this.$noticeContainer = null;
};
/**
* Get the html for this plugin's check license again link.
*
* @returns {string}
*/
Plugin.prototype.checkAgainLink = function() {
return ' <span class="as3cf-dbrains-check-my-licence-again"><a href="#">' + as3cf_dbrains.strings.check_licence_again + '</a></span>';
};
/**
* Does this plugin have any errors for its license?
*
* @returns {boolean}
*/
Plugin.prototype.hasLicenseErrors = function() {
const license = this.isAddon() ? this.parent.data.license : this.data.license;
return !_.isEmpty( license.errors );
};
/**
* Disable the plugin's list table checkbox.
*/
Plugin.prototype.disableUpdateCheckbox = function() {
if ( $listTable.is( '#update-plugins-table' ) ) {
this.$check.prop( {
disabled: true,
checked: false
} );
}
};
/**
* Enable the plugin's list table checkbox.
*/
Plugin.prototype.enableUpdateCheckbox = function() {
if ( $listTable.is( '#update-plugins-table' ) ) {
this.$check.prop( {
disabled: false
} );
}
};
/**
* Set the update notice with the given html.
*
* @param {string} html
*/
Plugin.prototype.setNotice = function( html ) {
this.$noticeContainer.find( '.as3cf-licence-notice' ).remove();
this.$noticeContainer.append(
'<div class="as3cf-licence-notice update-message notice inline notice-warning notice-alt">' +
'<p class="as3cf-before">' + html + this.checkAgainLink() + '</p>' +
'</div>'
);
};
/**
* Fade out our license notice and remove it, then restore WP update notices, if any.
*/
Plugin.prototype.clearNotice = function() {
const plugin = this;
const $notice = this.$noticeContainer.find( '.as3cf-licence-notice' );
$.when( $notice.fadeOut() ).done( function() {
$notice.remove();
plugin.$row.children().css( 'box-shadow', '' );
plugin.$noticeContainer.find( '.update-message' ).show();
} );
};
/**
* Render the plugin's state to the DOM.
*/
Plugin.prototype.render = function() {
if ( this.hasLicenseErrors() ) {
this.initNoticeContainer();
this.disableUpdateCheckbox();
this.$noticeContainer.find( '.update-message:not(".as3cf-licence-notice")' ).hide();
this.setNotice( this.getLicenseNotice() );
} else if ( this.$noticeContainer ) {
this.enableUpdateCheckbox();
this.clearNotice();
}
if ( !this.isAddon() ) {
_.invoke( this.addons, 'render' );
}
};
/**
* Initialize the notice container.
*/
Plugin.prototype.initNoticeContainer = function() {
if ( this.$noticeContainer ) {
return;
}
// Check if it is the Plugins update table
if ( !$listTable.is( '#update-plugins-table' ) ) {
// Check for an existing plugin-update notice row to use.
const $noticeRow = $listTable.find( '.plugin-update-tr[data-plugin="' + this.data.basename + '"]' );
if ( $noticeRow.length ) {
this.$noticeContainer = $noticeRow.find( '.plugin-update' );
} else {
const colspan = this.$row.children().length;
// If there is no plugin-update row, the plugin did not have an update, so inject a row to use.
this.$row.after(
'<tr class="plugin-update-tr ' + this.$row.attr( 'class' ) + '" ' +
'id="' + this.data.slug + '-update" ' +
'data-slug="' + this.data.slug + '" ' +
'data-plugin="' + this.data.basename + '" ' +
'>' +
'<td colspan="' + colspan + '" class="plugin-update colspanchange"></td>' +
'</tr>'
);
// The border between rows comes from a box-shadow on the children of the row above
this.$row.children().css( 'box-shadow', 'none' );
this.$noticeContainer = $listTable.find( '.plugin-update-tr[data-plugin="' + this.data.basename + '"] .plugin-update' );
}
} else {
// Updates
this.$noticeContainer = this.$row.find( '.plugin-title' );
}
// Finally, bind the click handler for ajax license checking
this.$noticeContainer.on( 'click', '.as3cf-dbrains-check-my-licence-again a', _.bind( this.checkLicenseAgain, this ) );
};
/**
* Get the text for the license notice.
*
* @returns {string}
*/
Plugin.prototype.getLicenseNotice = function() {
let message = '';
if ( this.data.update_available ) {
message = as3cf_dbrains.strings.update_available + ' ';
}
if ( this.isAddon() ) {
return message + as3cf_dbrains.strings.requires_parent_licence.replace( '%s', this.parent.data.name );
}
return message + _.values( this.data.license.errors ).join( '' );
};
/**
* Event listener callback to handle the check the license again for this plugin.
*
* @param {Event} event
*/
Plugin.prototype.checkLicenseAgain = function( event ) {
const plugin = this;
const $link = $( event.target );
const $spinner = $( '<span class="spinner is-active as3cf-dbrains-licence-check-spinner" style="float:none; margin-top: -3px;"></span>' );
event.preventDefault();
$link.hide().after( $spinner );
this.ajaxCheckLicense()
.done( function( data ) {
if ( plugin.isAddon() ) {
plugin.parent.data.license.errors = data.errors;
plugin.parent.render();
} else {
plugin.data.license.errors = data.errors;
plugin.render();
}
} )
.fail( function() {
plugin.setNotice( as3cf_dbrains.strings.licence_check_problem );
} )
.always( function() {
$spinner.remove();
} )
;
};
/**
* Send an ajax request to the server to check the plugin's license.
*
* @return {jqXHR} jQuery XHR Promise object
*/
Plugin.prototype.ajaxCheckLicense = function() {
const prefix = this.isAddon() ? this.parent.data.prefix : this.data.prefix;
return $.ajax( {
url: ajaxurl,
type: 'POST',
dataType: 'json',
cache: false,
data: {
action: prefix + '_check_licence',
nonce: as3cf_dbrains.nonces.check_licence
}
} );
};
/**
* Initialize the plugin instance.
*/
Plugin.prototype.init = function() {
this.render();
if ( !this.isAddon() ) {
this.initAddons();
}
};
/**
* Does this instance represent an add-on?
*
* @returns {boolean}
*/
Plugin.prototype.isAddon = function() {
return !!this.parent;
};
/**
* Initialize Plugin add-ons.
*/
Plugin.prototype.initAddons = function() {
const parent = this;
this.addons = _.map( this.data.addons, function( addonData ) {
return new Plugin( addonData, parent );
} );
_.invoke( this.addons, 'init' );
};
/**
* Instantiate and initialize plugin instances.
*/
function initialize() {
_.chain( as3cf_dbrains.plugins )
.map( function( pluginData ) {
return new Plugin( pluginData );
} )
.invoke( 'init' );
}
if ( $listTable.length ) {
$( initialize );
}
})( jQuery );

View File

@@ -0,0 +1 @@
(function($){const $listTable=$("#update-plugins-table,.wp-list-table.plugins");const Plugin=function(data,parent){this.data=data;this.parent=parent;this.$check=$listTable.find('.check-column [value="'+data.basename+'"]');this.$row=this.$check.closest("tr");this.$noticeContainer=null};Plugin.prototype.checkAgainLink=function(){return' <span class="as3cf-dbrains-check-my-licence-again"><a href="#">'+as3cf_dbrains.strings.check_licence_again+"</a></span>"};Plugin.prototype.hasLicenseErrors=function(){const license=this.isAddon()?this.parent.data.license:this.data.license;return!_.isEmpty(license.errors)};Plugin.prototype.disableUpdateCheckbox=function(){if($listTable.is("#update-plugins-table")){this.$check.prop({disabled:true,checked:false})}};Plugin.prototype.enableUpdateCheckbox=function(){if($listTable.is("#update-plugins-table")){this.$check.prop({disabled:false})}};Plugin.prototype.setNotice=function(html){this.$noticeContainer.find(".as3cf-licence-notice").remove();this.$noticeContainer.append('<div class="as3cf-licence-notice update-message notice inline notice-warning notice-alt">'+'<p class="as3cf-before">'+html+this.checkAgainLink()+"</p>"+"</div>")};Plugin.prototype.clearNotice=function(){const plugin=this;const $notice=this.$noticeContainer.find(".as3cf-licence-notice");$.when($notice.fadeOut()).done((function(){$notice.remove();plugin.$row.children().css("box-shadow","");plugin.$noticeContainer.find(".update-message").show()}))};Plugin.prototype.render=function(){if(this.hasLicenseErrors()){this.initNoticeContainer();this.disableUpdateCheckbox();this.$noticeContainer.find('.update-message:not(".as3cf-licence-notice")').hide();this.setNotice(this.getLicenseNotice())}else if(this.$noticeContainer){this.enableUpdateCheckbox();this.clearNotice()}if(!this.isAddon()){_.invoke(this.addons,"render")}};Plugin.prototype.initNoticeContainer=function(){if(this.$noticeContainer){return}if(!$listTable.is("#update-plugins-table")){const $noticeRow=$listTable.find('.plugin-update-tr[data-plugin="'+this.data.basename+'"]');if($noticeRow.length){this.$noticeContainer=$noticeRow.find(".plugin-update")}else{const colspan=this.$row.children().length;this.$row.after('<tr class="plugin-update-tr '+this.$row.attr("class")+'" '+'id="'+this.data.slug+'-update" '+'data-slug="'+this.data.slug+'" '+'data-plugin="'+this.data.basename+'" '+">"+'<td colspan="'+colspan+'" class="plugin-update colspanchange"></td>'+"</tr>");this.$row.children().css("box-shadow","none");this.$noticeContainer=$listTable.find('.plugin-update-tr[data-plugin="'+this.data.basename+'"] .plugin-update')}}else{this.$noticeContainer=this.$row.find(".plugin-title")}this.$noticeContainer.on("click",".as3cf-dbrains-check-my-licence-again a",_.bind(this.checkLicenseAgain,this))};Plugin.prototype.getLicenseNotice=function(){let message="";if(this.data.update_available){message=as3cf_dbrains.strings.update_available+" "}if(this.isAddon()){return message+as3cf_dbrains.strings.requires_parent_licence.replace("%s",this.parent.data.name)}return message+_.values(this.data.license.errors).join("")};Plugin.prototype.checkLicenseAgain=function(event){const plugin=this;const $link=$(event.target);const $spinner=$('<span class="spinner is-active as3cf-dbrains-licence-check-spinner" style="float:none; margin-top: -3px;"></span>');event.preventDefault();$link.hide().after($spinner);this.ajaxCheckLicense().done((function(data){if(plugin.isAddon()){plugin.parent.data.license.errors=data.errors;plugin.parent.render()}else{plugin.data.license.errors=data.errors;plugin.render()}})).fail((function(){plugin.setNotice(as3cf_dbrains.strings.licence_check_problem)})).always((function(){$spinner.remove()}))};Plugin.prototype.ajaxCheckLicense=function(){const prefix=this.isAddon()?this.parent.data.prefix:this.data.prefix;return $.ajax({url:ajaxurl,type:"POST",dataType:"json",cache:false,data:{action:prefix+"_check_licence",nonce:as3cf_dbrains.nonces.check_licence}})};Plugin.prototype.init=function(){this.render();if(!this.isAddon()){this.initAddons()}};Plugin.prototype.isAddon=function(){return!!this.parent};Plugin.prototype.initAddons=function(){const parent=this;this.addons=_.map(this.data.addons,(function(addonData){return new Plugin(addonData,parent)}));_.invoke(this.addons,"init")};function initialize(){_.chain(as3cf_dbrains.plugins).map((function(pluginData){return new Plugin(pluginData)})).invoke("init")}if($listTable.length){$(initialize)}})(jQuery);

View File

@@ -0,0 +1 @@
{"version":3,"names":["$","$listTable","Plugin","data","parent","this","$check","find","basename","$row","closest","$noticeContainer","prototype","checkAgainLink","as3cf_dbrains","strings","check_licence_again","hasLicenseErrors","license","isAddon","_","isEmpty","errors","disableUpdateCheckbox","is","prop","disabled","checked","enableUpdateCheckbox","setNotice","html","remove","append","clearNotice","plugin","$notice","when","fadeOut","done","children","css","show","render","initNoticeContainer","hide","getLicenseNotice","invoke","addons","$noticeRow","length","colspan","after","attr","slug","on","bind","checkLicenseAgain","message","update_available","requires_parent_licence","replace","name","values","join","event","$link","target","$spinner","preventDefault","ajaxCheckLicense","fail","licence_check_problem","always","prefix","ajax","url","ajaxurl","type","dataType","cache","action","nonce","nonces","check_licence","init","initAddons","map","addonData","initialize","chain","plugins","pluginData","jQuery"],"sources":["vendor/deliciousbrains/assets/js/plugin-update.js"],"mappings":"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","ignoreList":[]}