v0.7.4 — wp.org submission cleanup: remove updater + add LICENSE
Walks back v0.7.3's Update URI guard pattern. Plugin Check raised
`plugin_updater_detected` on the v0.7.3 build:
"Including An Update Checker / Changing Updates functionality.
Plugin Updater detected. Use of the Update URI header is not
allowed in plugins hosted on WordPress.org."
PCP scans the source as-shipped, not as-distributed, so the v0.7.3
build-time `sed` strip never had a chance to run before the scan.
The simpler correct answer is to delete the custom updater entirely
and rely on wp.org as the canonical update channel once accepted.
Removed:
* `inc/updater.php` — recoverable from git history at tag v0.7.3 if
ever needed for a non-wp.org distribution.
* `Update URI:` header line in `radio.php` (plus the NOTE block).
* `require_once RADIO_PATH . 'inc/updater.php';` in `radio.php`.
* Updates panel render + `function_exists()` guard in `inc/settings.php`.
* "Self-hosted update checker" line in `README.md`.
* "Self-hosted updater" bullet in `readme.txt` Privacy section.
Added (GPL declaration loop closed):
* `LICENSE` — verbatim canonical GPL v2 text (338 lines from
gnu.org/licenses/gpl-2.0.txt).
* GPL header block in `assets/css/radio.css`.
* GPL header block in `assets/js/radio.js` (original module overview
preserved verbatim below the license header).
* GPL header block in `radio.php` docblock alongside the existing
`License:` / `License URI:` fields.
Migration: existing Gitea-installed copies of v0.7.3 or earlier
become orphaned of auto-updates after this lands on them (the
updater code is gone, so nothing advertises newer versions back).
Recommended path is to uninstall + reinstall from wp.org once the
plugin is accepted. No data loss — station + volume + theme +
history + favourites all live in user_meta.
No user-visible behaviour changes for the player itself. Only the
small `Updates` panel that sat at the bottom of Radio → Settings
is gone.
This commit is contained in:
+3
-2
@@ -79,14 +79,15 @@ function radio_render_about_page() {
|
||||
<h2><?php esc_html_e( 'Version history', 'a-radio' ); ?></h2>
|
||||
|
||||
<div class="radio-about-versions__latest">
|
||||
<span class="ver">v0.7.3</span> — 30 May 2026 <span class="latest"><?php esc_html_e( 'latest', 'a-radio' ); ?></span>
|
||||
<span class="ver">v0.7.4</span> — 30 May 2026 <span class="latest"><?php esc_html_e( 'latest', 'a-radio' ); ?></span>
|
||||
<p>
|
||||
<?php esc_html_e( 'WordPress.org guideline 8 compliance + Privacy documentation. Added Update URI header so installs from the author\'s Gitea keep receiving updates from there, while wp.org-distributed copies (where the build strips that header line) hand update delivery to WordPress.org — the self-hosted updater short-circuits at load time and the Updates panel hides automatically. Added a dedicated Privacy section to readme covering every outbound connection (none from the plugin itself; SomaFM audio + 30-second songs.json poll only while playing). Added explicit link to SomaFM terms of use. No user-visible behaviour changes.', 'a-radio' ); ?>
|
||||
<?php esc_html_e( 'WordPress.org submission cleanup. Removed the self-hosted Gitea updater (`inc/updater.php`) and the `Update URI` header from `radio.php` — WordPress.org explicitly disallows both for hosted plugins (Plugin Check `plugin_updater_detected`). Updates now flow through WordPress.org as the canonical channel. Added a top-level LICENSE file with the full GPL v2 text and GPL header blocks to the CSS and JS assets so the licensing of every shipped file is explicit. No user-visible behaviour changes for the player — only the small `Updates` panel that used to sit at the bottom of Radio → Settings is gone.', 'a-radio' ); ?>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h3><?php esc_html_e( 'Earlier releases', 'a-radio' ); ?></h3>
|
||||
<ul class="radio-about-versions__earlier">
|
||||
<li><span class="ver">v0.7.3</span> <span class="ver-date">30 May 2026</span> — <?php esc_html_e( 'Privacy section in readme + SomaFM terms-of-use link (Update URI guard pattern from this release was walked back in v0.7.4 — Plugin Check disallows it for wp.org-hosted plugins regardless of header value)', 'a-radio' ); ?></li>
|
||||
<li><span class="ver">v0.7.2</span> <span class="ver-date">30 May 2026</span> — <?php esc_html_e( 'Screenshots + correct wp.org contributor handle (ir240474)', 'a-radio' ); ?></li>
|
||||
<li><span class="ver">v0.7.1</span> <span class="ver-date">30 May 2026</span> — <?php esc_html_e( 'Plugin Check follow-up — Tested-up-to bumped to 7.0, .DS_Store re-cleanup', 'a-radio' ); ?></li>
|
||||
<li><span class="ver">v0.7.0</span> <span class="ver-date">30 May 2026</span> — <?php esc_html_e( 'WordPress.org submission prep — full Plugin Check clean (169 → 4 issues, branding, textdomain, security, popup refactor, readme.txt)', 'a-radio' ); ?></li>
|
||||
|
||||
+1
-13
@@ -3,8 +3,7 @@
|
||||
* Radio — Settings page.
|
||||
*
|
||||
* Lets the user pick default station + volume + theme + dashboard
|
||||
* widget opt-out. Renders the Updates panel from `updater.php` at the
|
||||
* bottom.
|
||||
* widget opt-out.
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) { exit; }
|
||||
@@ -121,17 +120,6 @@ function radio_render_settings_page() {
|
||||
|
||||
<?php submit_button( __( 'Save Changes', 'a-radio' ), 'primary', 'radio_settings_submit' ); ?>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
// Updates panel — only manage_options users see it, and only when
|
||||
// the custom updater is active (i.e. self-hosted Gitea install,
|
||||
// NOT when distributed via wp.org where wp.org handles updates).
|
||||
// The function is undefined when updater.php short-circuited at
|
||||
// load time per wp.org guideline 8 — hence the function_exists() check.
|
||||
if ( current_user_can( 'manage_options' ) && function_exists( 'radio_render_updates_panel' ) ) {
|
||||
radio_render_updates_panel();
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
-281
@@ -1,281 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Radio — self-hosted update checker against the Gitea repo.
|
||||
*
|
||||
* Direct port of the Buddy / Logbook updater (proven in production).
|
||||
* Polls Gitea's /releases/latest, falls back to /tags?limit=1 when no
|
||||
* formal Release object exists, compares against RADIO_VERSION,
|
||||
* renders an Updates panel on the Settings page. Cached 12h on
|
||||
* success, 1h on negative responses.
|
||||
*
|
||||
* Repo coordinates are constants you can override via define() in
|
||||
* wp-config.php if the repo ever moves.
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) { exit; }
|
||||
|
||||
/**
|
||||
* v0.7.3 — wp.org guideline 8 guard.
|
||||
*
|
||||
* If the plugin's `Update URI:` header is empty or points to wordpress.org,
|
||||
* the plugin is being distributed via WordPress.org and core handles updates.
|
||||
* In that case the self-hosted updater MUST stay dormant — serving updates
|
||||
* from a non-wp.org server is explicitly prohibited by guideline 8.
|
||||
*
|
||||
* When `Update URI` points at our Gitea (the default for self-hosted /
|
||||
* pre-submission installs), wp.org skips this plugin and our updater runs
|
||||
* normally.
|
||||
*
|
||||
* The submission build script strips the `Update URI:` line from
|
||||
* `radio.php` so this check trips and the entire updater becomes a no-op.
|
||||
*/
|
||||
function radio_should_skip_custom_updater() {
|
||||
static $cached = null;
|
||||
if ( null !== $cached ) { return $cached; }
|
||||
if ( ! defined( 'RADIO_FILE' ) || ! function_exists( 'get_file_data' ) ) {
|
||||
$cached = false;
|
||||
return $cached;
|
||||
}
|
||||
$data = get_file_data( RADIO_FILE, array( 'UpdateURI' => 'Update URI' ) );
|
||||
$uri = isset( $data['UpdateURI'] ) ? trim( $data['UpdateURI'] ) : '';
|
||||
if ( '' === $uri ) { $cached = true; return $cached; } // empty → wp.org default
|
||||
if ( false !== stripos( $uri, 'wordpress.org' ) ) { $cached = true; return $cached; } // explicit wp.org
|
||||
$cached = false; // points at Gitea / other → custom updater runs
|
||||
return $cached;
|
||||
}
|
||||
|
||||
// Short-circuit: stop here entirely if wp.org is handling updates.
|
||||
if ( radio_should_skip_custom_updater() ) { return; }
|
||||
|
||||
if ( ! defined( 'RADIO_GITEA_HOST' ) ) { define( 'RADIO_GITEA_HOST', 'https://git.davidtkeane.com' ); }
|
||||
if ( ! defined( 'RADIO_GITEA_OWNER' ) ) { define( 'RADIO_GITEA_OWNER', 'ranger' ); }
|
||||
if ( ! defined( 'RADIO_GITEA_REPO' ) ) { define( 'RADIO_GITEA_REPO', 'a-radio' ); }
|
||||
|
||||
function radio_gitea_repo_url() {
|
||||
return RADIO_GITEA_HOST . '/' . RADIO_GITEA_OWNER . '/' . RADIO_GITEA_REPO;
|
||||
}
|
||||
function radio_gitea_releases_url() {
|
||||
return radio_gitea_repo_url() . '/releases';
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the latest release/tag, normalised. Returns null on hard
|
||||
* error, or an array including `version`.
|
||||
*/
|
||||
function radio_fetch_latest_release( $force_refresh = false ) {
|
||||
$cache_key = 'radio_gitea_latest';
|
||||
|
||||
if ( ! $force_refresh ) {
|
||||
$cached = get_site_transient( $cache_key );
|
||||
if ( is_array( $cached ) ) { return $cached; }
|
||||
}
|
||||
|
||||
$base_api = RADIO_GITEA_HOST . '/api/v1/repos/' . RADIO_GITEA_OWNER . '/' . RADIO_GITEA_REPO;
|
||||
|
||||
// Try formal Release first.
|
||||
$response = wp_remote_get( $base_api . '/releases/latest', array( 'timeout' => 8 ) );
|
||||
if ( is_wp_error( $response ) ) { return null; }
|
||||
|
||||
$code = (int) wp_remote_retrieve_response_code( $response );
|
||||
$body = ( $code === 200 ) ? json_decode( wp_remote_retrieve_body( $response ), true ) : null;
|
||||
|
||||
// Fallback to /tags if no Release object exists yet.
|
||||
if ( $code !== 200 || ! is_array( $body ) || empty( $body['tag_name'] ) ) {
|
||||
$tags_response = wp_remote_get( $base_api . '/tags?limit=1', array( 'timeout' => 8 ) );
|
||||
if ( ! is_wp_error( $tags_response )
|
||||
&& (int) wp_remote_retrieve_response_code( $tags_response ) === 200 ) {
|
||||
$tags = json_decode( wp_remote_retrieve_body( $tags_response ), true );
|
||||
if ( is_array( $tags ) && ! empty( $tags[0]['name'] ) ) {
|
||||
$body = array(
|
||||
'tag_name' => $tags[0]['name'],
|
||||
'html_url' => radio_gitea_repo_url() . '/src/tag/' . rawurlencode( $tags[0]['name'] ),
|
||||
'body' => isset( $tags[0]['message'] ) ? $tags[0]['message'] : '',
|
||||
'published_at' => isset( $tags[0]['commit']['created'] ) ? $tags[0]['commit']['created'] : null,
|
||||
'assets' => array(),
|
||||
);
|
||||
$code = 200;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( $code !== 200 || ! is_array( $body ) || empty( $body['tag_name'] ) ) {
|
||||
$info = array(
|
||||
'version' => null,
|
||||
'html_url' => radio_gitea_releases_url(),
|
||||
'download_url' => null,
|
||||
'body' => '',
|
||||
'published_at' => null,
|
||||
'error_code' => $code,
|
||||
);
|
||||
set_site_transient( $cache_key, $info, HOUR_IN_SECONDS );
|
||||
return $info;
|
||||
}
|
||||
|
||||
$version = ltrim( (string) $body['tag_name'], 'vV' );
|
||||
|
||||
// Prefer a .zip asset; fall back to Gitea source-archive URL.
|
||||
$download_url = null;
|
||||
if ( ! empty( $body['assets'] ) && is_array( $body['assets'] ) ) {
|
||||
foreach ( $body['assets'] as $asset ) {
|
||||
if ( isset( $asset['name'], $asset['browser_download_url'] )
|
||||
&& substr( strtolower( $asset['name'] ), -4 ) === '.zip' ) {
|
||||
$download_url = $asset['browser_download_url'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( ! $download_url ) {
|
||||
$download_url = radio_gitea_repo_url() . '/archive/' . rawurlencode( $body['tag_name'] ) . '.zip';
|
||||
}
|
||||
|
||||
$info = array(
|
||||
'version' => $version,
|
||||
'html_url' => isset( $body['html_url'] ) ? esc_url_raw( $body['html_url'] ) : '',
|
||||
'download_url' => esc_url_raw( $download_url ),
|
||||
'body' => isset( $body['body'] ) ? wp_strip_all_tags( $body['body'] ) : '',
|
||||
'published_at' => isset( $body['published_at'] ) ? $body['published_at'] : null,
|
||||
);
|
||||
|
||||
set_site_transient( $cache_key, $info, 12 * HOUR_IN_SECONDS );
|
||||
return $info;
|
||||
}
|
||||
|
||||
function radio_update_status( $force_refresh = false ) {
|
||||
$current = defined( 'RADIO_VERSION' ) ? RADIO_VERSION : '0.0.0';
|
||||
$latest = radio_fetch_latest_release( $force_refresh );
|
||||
|
||||
if ( ! $latest || empty( $latest['version'] ) ) {
|
||||
$msg = __( 'No releases tagged on the Gitea repo yet.', 'a-radio' );
|
||||
if ( $latest && ! empty( $latest['error_code'] ) && (int) $latest['error_code'] !== 404 ) {
|
||||
/* translators: %d = HTTP status code returned by the Gitea API */
|
||||
$msg = sprintf( __( 'Could not reach Gitea (HTTP %d). Try again in a few minutes.', 'a-radio' ), (int) $latest['error_code'] );
|
||||
}
|
||||
return array(
|
||||
'status' => 'unknown',
|
||||
'current' => $current,
|
||||
'message' => $msg,
|
||||
'repo_url' => radio_gitea_repo_url(),
|
||||
);
|
||||
}
|
||||
|
||||
if ( version_compare( $latest['version'], $current, '>' ) ) {
|
||||
return array(
|
||||
'status' => 'available',
|
||||
'current' => $current,
|
||||
'latest' => $latest['version'],
|
||||
'html_url' => $latest['html_url'],
|
||||
'download_url' => $latest['download_url'],
|
||||
'published_at' => $latest['published_at'],
|
||||
'body' => $latest['body'],
|
||||
/* translators: 1: latest version available; 2: version currently installed */
|
||||
'message' => sprintf( __( 'A new version (v%1$s) is available — you are on v%2$s.', 'a-radio' ), $latest['version'], $current ),
|
||||
);
|
||||
}
|
||||
|
||||
return array(
|
||||
'status' => 'up-to-date',
|
||||
'current' => $current,
|
||||
'latest' => $latest['version'],
|
||||
/* translators: %s = current installed version */
|
||||
'message' => sprintf( __( 'You are up to date (v%s).', 'a-radio' ), $current ),
|
||||
'repo_url' => radio_gitea_repo_url(),
|
||||
);
|
||||
}
|
||||
|
||||
add_action( 'wp_ajax_radio_check_updates', 'radio_ajax_check_updates' );
|
||||
function radio_ajax_check_updates() {
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_send_json_error( 'Insufficient permissions.', 403 );
|
||||
}
|
||||
check_ajax_referer( 'radio_check_updates', 'nonce' );
|
||||
delete_site_transient( 'radio_gitea_latest' );
|
||||
wp_send_json_success( radio_update_status( true ) );
|
||||
}
|
||||
|
||||
function radio_render_updates_panel() {
|
||||
$status = radio_update_status( false );
|
||||
$nonce = wp_create_nonce( 'radio_check_updates' );
|
||||
$repo_url = radio_gitea_repo_url();
|
||||
$rel_url = radio_gitea_releases_url();
|
||||
?>
|
||||
<div class="radio-updates" style="max-width:720px; margin-top:24px; padding:18px 20px; background:#fff; border:1px solid #ccd0d4; border-radius:4px;">
|
||||
<h2 style="margin-top:0;"><?php esc_html_e( 'Updates', 'a-radio' ); ?></h2>
|
||||
<p style="margin:0 0 12px;">
|
||||
<?php esc_html_e( 'Radio is self-hosted on Gitea. Click Check now to ask the repo whether there is a newer release than the one you are running.', 'a-radio' ); ?>
|
||||
</p>
|
||||
|
||||
<p id="radio-update-status" style="margin:0 0 12px;">
|
||||
<strong><?php esc_html_e( 'Status:', 'a-radio' ); ?></strong>
|
||||
<span id="radio-update-status-text"><?php echo esc_html( $status['message'] ); ?></span>
|
||||
<?php if ( $status['status'] === 'available' && ! empty( $status['download_url'] ) ) : ?>
|
||||
<br>
|
||||
<a href="<?php echo esc_url( $status['download_url'] ); ?>" class="button button-primary" style="margin-top:8px;">
|
||||
<?php
|
||||
/* translators: %s is the latest version number, e.g. "0.2.0" */
|
||||
/* translators: %s = latest available version number, e.g. "0.7.0" */
|
||||
echo esc_html( sprintf( __( 'Download v%s (.zip)', 'a-radio' ), $status['latest'] ) );
|
||||
?>
|
||||
</a>
|
||||
<?php if ( ! empty( $status['html_url'] ) ) : ?>
|
||||
<a href="<?php echo esc_url( $status['html_url'] ); ?>" target="_blank" rel="noopener" style="margin-left:8px;"><?php esc_html_e( 'View release notes →', 'a-radio' ); ?></a>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
</p>
|
||||
|
||||
<p style="margin:0 0 4px;">
|
||||
<button type="button" id="radio-check-updates-btn" class="button" data-nonce="<?php echo esc_attr( $nonce ); ?>">
|
||||
↻ <?php esc_html_e( 'Check now', 'a-radio' ); ?>
|
||||
</button>
|
||||
<a href="<?php echo esc_url( $repo_url ); ?>" target="_blank" rel="noopener" class="button" style="margin-left:6px;"><?php esc_html_e( 'View on Gitea', 'a-radio' ); ?></a>
|
||||
<a href="<?php echo esc_url( $rel_url ); ?>" target="_blank" rel="noopener" class="button" style="margin-left:6px;"><?php esc_html_e( 'View all releases', 'a-radio' ); ?></a>
|
||||
</p>
|
||||
<p style="margin:10px 0 0; color:#646970; font-size:12px;">
|
||||
<?php esc_html_e( 'Manual update path: download the .zip, deactivate the plugin in WordPress, upload via Plugins → Add New → Upload, reactivate. Your settings survive the upgrade (state is stored in user_meta).', 'a-radio' ); ?>
|
||||
</p>
|
||||
|
||||
<?php if ( defined( 'RADIO_SUPPORT_URL' ) && RADIO_SUPPORT_URL ) : ?>
|
||||
<a class="radio-support-link" href="<?php echo esc_url( RADIO_SUPPORT_URL ); ?>" target="_blank" rel="noopener">
|
||||
<?php esc_html_e( 'Like Radio? If You fancy to buy me a coffee →', 'a-radio' ); ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
var btn = document.getElementById('radio-check-updates-btn');
|
||||
var statusText = document.getElementById('radio-update-status-text');
|
||||
if (!btn || !statusText) { return; }
|
||||
|
||||
btn.addEventListener('click', function () {
|
||||
var nonce = btn.getAttribute('data-nonce');
|
||||
btn.disabled = true;
|
||||
var orig = btn.textContent;
|
||||
btn.textContent = '↻ Checking…';
|
||||
statusText.textContent = 'Asking Gitea…';
|
||||
|
||||
var fd = new FormData();
|
||||
fd.append('action', 'radio_check_updates');
|
||||
fd.append('nonce', nonce);
|
||||
|
||||
fetch(ajaxurl, { method: 'POST', credentials: 'same-origin', body: fd })
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (res) {
|
||||
if (!res || !res.success) {
|
||||
statusText.textContent = (res && res.data) ? String(res.data) : 'Check failed.';
|
||||
} else {
|
||||
statusText.textContent = res.data.message || 'Check complete.';
|
||||
if (res.data.status === 'available' && res.data.download_url) {
|
||||
setTimeout(function () { window.location.reload(); }, 400);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(function () { statusText.textContent = 'Network error — try again in a moment.'; })
|
||||
.finally(function () {
|
||||
btn.disabled = false;
|
||||
btn.textContent = orig;
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
Reference in New Issue
Block a user