Skip to content

Instantly share code, notes, and snippets.

@vielhuber
Last active May 10, 2021 22:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vielhuber/1a14bbed2d2c54f7686bcce16c80ed59 to your computer and use it in GitHub Desktop.
Save vielhuber/1a14bbed2d2c54f7686bcce16c80ed59 to your computer and use it in GitHub Desktop.
sell and remote update free pro release premium plugins #wordpress

preparation

server

plugin

  • add logic to your plugins code (see test.php for a full example)
  • zip test/test.php to test.zip
  • install plugin manually (for the first time, to test further updates)

workflow

make a new release

  • update plugin code
  • zip test/test.php to test.zip
  • make an api call to
    • https://tld.com/wp-json/v1/release
    • Basic Auth: username and password of an administrator
    • POST data
      • name: Test
      • version: 1.0.1
      • required: 5
      • tested: 5.7
      • file: base64 string of updated plugin zip
      • icon: base64 string of plugin icon
class ServerPluginDeploy
{
public function __construct()
{
$this->makeNewRelease();
$this->checkForUpdates();
$this->showLicenseDomains();
}
private function makeNewRelease()
{
add_action('rest_api_init', function () {
register_rest_route('v1', '/release', [
'methods' => \WP_REST_Server::CREATABLE,
'permission_callback' => function () {
$username = @$_SERVER['PHP_AUTH_USER'];
$password = @$_SERVER['PHP_AUTH_PW'];
if ($username == '' || $password == '') {
return false;
}
$user = wp_authenticate($username, $password);
if (is_wp_error($user)) {
return false;
}
wp_set_current_user($user->ID);
return current_user_can('manage_options');
},
'callback' => function (\WP_REST_Request $request) {
$input = $request->get_params();
if (
!isset($input['name']) ||
$input['name'] == '' ||
!isset($input['version']) ||
$input['version'] == '' ||
!isset($input['requires']) ||
$input['requires'] == '' ||
!isset($input['tested']) ||
$input['tested'] == '' ||
!isset($input['file']) ||
$input['file'] == '' ||
!isset($input['icon']) ||
$input['icon'] == ''
) {
return new \WP_REST_Response(
[
'success' => false,
'message' => 'server error'
],
404
);
}
$filename = md5(uniqid(mt_rand(), true));
$folder = wp_upload_dir()['basedir'] . '/release/';
$folder_public = wp_upload_dir()['baseurl'] . '/release/';
if (!is_dir($folder)) {
@mkdir($folder);
}
foreach (glob($folder . '*') as $files__value) {
if (is_file($files__value)) {
@unlink($files__value);
}
}
if (!file_exists($folder . '.gitignore')) {
file_put_contents($folder . '.gitignore', '*');
}
file_put_contents($folder . $filename . '.zip', base64_decode($input['file']));
file_put_contents($folder . $filename . '.png', base64_decode($input['icon']));
file_put_contents(
$folder . $filename . '.txt',
serialize([
'name' => $input['name'],
'version' => $input['version'],
'requires' => $input['requires'],
'tested' => $input['tested'],
'download_url' => $folder_public . $filename . '.zip',
'icon' => $folder_public . $filename . '.png'
])
);
// update wordpress downloadable zip url
global $wpdb;
$files = $wpdb->get_results(
$wpdb->prepare(
'SELECT * FROM ' . $wpdb->prefix . 'postmeta WHERE meta_key = %s',
'_downloadable_files'
)
);
if (!empty($files)) {
foreach ($files as $files__value) {
$files_meta_value = $files__value->meta_value;
$files_meta_value = @unserialize($files_meta_value);
if (!empty($files_meta_value)) {
foreach ($files_meta_value as $files_meta_value__key => $files_meta_value__value) {
$files_meta_value[$files_meta_value__key]['file'] =
$folder_public . $filename . '.zip';
}
}
$files_meta_value = serialize($files_meta_value);
$wpdb->update(
$wpdb->prefix . 'postmeta',
['meta_value' => $files_meta_value],
['meta_id' => $files__value->meta_id]
);
}
}
return new \WP_REST_Response(
[
'success' => true,
'message' => 'successfully made a new release',
'data' => [
'url' => $folder_public . $filename . '.zip'
]
],
200
);
}
]);
});
}
private function checkForUpdates()
{
add_action('rest_api_init', function () {
register_rest_route('v1', '/update', [
'methods' => \WP_REST_Server::CREATABLE,
'permission_callback' => '__return_true',
'callback' => function (\WP_REST_Request $request) {
$key = $request->get_param('key');
$domain = $request->get_param('domain');
$file = glob(wp_upload_dir()['basedir'] . '/release/*.txt');
// error
if ($key == '' || $domain == '' || !lmfwc_get_license($key) || empty($file)) {
return new \WP_REST_Response(
[
'success' => false,
'message' => 'server error',
'public_message' => 'Es ist ein Fehler aufgetreten.'
],
404
);
}
// log domains
$license = lmfwc_get_license($key);
$domains = lmfwc_get_license_meta($license->getId(), 'domains', false);
if (!in_array($domain, $domains)) {
$domains[] = $domain;
lmfwc_add_license_meta($license->getId(), 'domains', $domain);
}
// return update data
return new \WP_REST_Response(unserialize(file_get_contents($file[0])), 200);
}
]);
});
}
private function showLicenseDomains()
{
add_filter(
'gettext',
function ($translation, $text, $domain) {
if (
is_admin() &&
isset($_GET['page']) &&
isset($_GET['action']) &&
isset($_GET['id']) &&
$_GET['page'] === 'lmfwc_licenses' &&
$_GET['action'] === 'edit' &&
$_GET['id'] != '' &&
$text === 'The license key will be encrypted before it is stored inside the database.' &&
$domain === 'license-manager-for-woocommerce' &&
!empty(($meta = lmfwc_get_license_meta($_GET['id'], 'domains')))
) {
$translation = 'Domains: ' . implode(', ', $meta);
}
return $translation;
},
PHP_INT_MAX,
3
);
}
}
new ServerPluginDeploy();
<?php
/**
* Plugin Name: Test
* Plugin URI: https://test.de
* Description: A simple test.
* Version: 1.0.0
* Author: David Vielhuber
* Author URI: https://vielhuber.de
* License: free
*/
class TestPlugin
{
public function __construct() {
$this->init();
$this->checkForUpdates();
}
private function init() {
// dummy logic
add_action( 'admin_init', function() {
add_filter( 'admin_footer_text', function($content) {
return 'Test plugin 1.0.0 active!';
}, 11 );
});
// this should be editable in wp backend
update_option('test_license_key', '3WVPLU-W4GZJ9-9PNU74-5PROU2');
}
private function checkForUpdates() {
// code inspired from https://rudrastyh.com/wordpress/self-hosted-plugin-update.html
global $custom_options;
$custom_options = [
'plugin_slug' => 'test',
'update_url' => 'https://tld.com/wp-json/v1/update',
'license_key' => get_option('test_license_key'),
'cache_time' => 3 // debug
];
// check license key
if ($custom_options['license_key'] == '') {
return;
}
// allow http without ssl in download url
add_filter( 'http_request_args', function ( $args ) {
$args['reject_unsafe_urls'] = false;
return $args;
}, 999 );
// check for plugin updates
add_filter('site_transient_update_plugins', function ($transient) {
global $custom_options;
if (empty($transient->checked)) {
return $transient;
}
$remote = get_transient('custom_upgrade_' . $custom_options['plugin_slug']);
if ($remote == false) {
$remote = wp_remote_post($custom_options['update_url'], [
'body' => wp_json_encode([
'key' => $custom_options['license_key'],
'domain' => $_SERVER['HTTP_HOST']
]),
'headers' => [
'Content-Type' => 'application/json'
],
'timeout' => 10
]);
if (
!is_wp_error($remote) &&
isset($remote['response']['code']) &&
$remote['response']['code'] == 200 &&
!empty($remote['body'])
) {
set_transient(
'custom_upgrade_' . $custom_options['plugin_slug'],
$remote,
$custom_options['cache_time']
);
}
}
if (
!is_wp_error($remote) &&
isset($remote['response']['code']) &&
$remote['response']['code'] == 200 &&
!empty($remote['body'])
) {
$remote = json_decode($remote['body']);
if (
$remote &&
version_compare(
get_file_data(__FILE__, ['Version' => 'Version'], false)['Version'],
$remote->version,
'<'
) &&
version_compare($remote->requires, get_bloginfo('version'), '<')
) {
$res = new stdClass();
$res->slug = $custom_options['plugin_slug'];
$res->plugin = $custom_options['plugin_slug'] . '/' . $custom_options['plugin_slug'] . '.php';
$res->new_version = $remote->version;
$res->tested = $remote->tested;
$res->package = $remote->download_url;
$res->icons = [
'1x' => $remote->icon,
'2x' => $remote->icon
];
$transient->response[$res->plugin] = $res;
}
}
return $transient;
});
// provide detail infos
add_filter(
'plugins_api',
function ($res, $action, $args) {
global $custom_options;
if ($action !== 'plugin_information') {
return false;
}
if ($args->slug !== $custom_options['plugin_slug']) {
return false;
}
$remote = get_transient('custom_update_' . $custom_options['plugin_slug']);
if ($remote == false) {
$remote = wp_remote_post($custom_options['update_url'], [
'body' => wp_json_encode([
'key' => $custom_options['license_key'],
'domain' => $_SERVER['HTTP_HOST']
]),
'headers' => [
'Content-Type' => 'application/json'
],
'timeout' => 10
]);
if (
!is_wp_error($remote) &&
isset($remote['response']['code']) &&
$remote['response']['code'] == 200 &&
!empty($remote['body'])
) {
set_transient(
'custom_update_' . $custom_options['plugin_slug'],
$remote,
$custom_options['cache_time']
);
}
}
if (
!is_wp_error($remote) &&
isset($remote['response']['code']) &&
$remote['response']['code'] == 200 &&
!empty($remote['body'])
) {
$remote = json_decode($remote['body']);
$res = new stdClass();
$res->name = $remote->name;
$res->slug = $custom_options['plugin_slug'];
$res->version = $remote->version;
$res->tested = $remote->tested;
$res->download_link = $remote->download_url;
$res->trunk = $remote->download_url;
$res->sections = [];
return $res;
}
return false;
},
20,
3
);
// clean cache after plugin update
add_action(
'upgrader_process_complete',
function ($upgrader_object, $options) {
global $custom_options;
if ($options['action'] == 'update' && $options['type'] === 'plugin') {
delete_transient('custom_upgrade_' . $custom_options['plugin_slug']);
}
},
10,
2
);
}
}
new TestPlugin();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment