Skip to content

Instantly share code, notes, and snippets.

@westonruter
Last active February 21, 2024 01:19
Show Gist options
  • Save westonruter/0d09371a2b2c991f344fa67b728975c5 to your computer and use it in GitHub Desktop.
Save westonruter/0d09371a2b2c991f344fa67b728975c5 to your computer and use it in GitHub Desktop.
<?php
/**
* Plugin Name: Admin PWA
* Plugin URI: https://github.com/GoogleChromeLabs/pwa-wp/issues/295
* Description: Proof of concept to turn the WordPress admin into a PWA.
* Version: 0.1.0
* Author: Weston Ruter
* Author URI: https://weston.ruter.net/
* Text Domain: pwa
* License: GPL-2.0+
* License URI: http://www.gnu.org/licenses/gpl-2.0.txt
* Requires Plugins: pwa
* Requires PHP: 5.6
*/
namespace AdminPWA;
/**
* Gets manifest overrides which are merged on top of the non-admin manifest.
*
* @return array<string, mixed> Manifest overrides.
*/
function get_admin_web_app_manifest_overrides(): array {
$manifest = array();
$manifest['start_url'] = admin_url( '/' );
$manifest['scope'] = admin_url( '/' );
$manifest['display'] = 'standalone';
// Add shortcuts for adding new items of each public post type.
foreach ( get_post_types( array( 'public' => true ), 'objects' ) as $post_type ) {
if ( 'attachment' === $post_type->name || ! current_user_can( $post_type->cap->create_posts ) ) {
continue;
}
$manifest['shortcuts'][] = array(
'name' => $post_type->labels->new_item,
'url' => add_query_arg( 'post_type', $post_type->name, admin_url( 'post-new.php' ) ),
// TODO: Add 'icons'.
);
}
return $manifest;
}
add_action(
'admin_head',
static function () {
global $wp_web_app_manifest;
if ( empty( $wp_web_app_manifest ) || ! method_exists( $wp_web_app_manifest, 'get_manifest' ) ) {
// Bail if PWA plugin is not active or the class name changed in core merge.
return;
}
$manifest = array_merge( $wp_web_app_manifest->get_manifest(), get_admin_web_app_manifest_overrides() );
// Props ellatrix for the idea to use a data: URL for the web app manifest.
?>
<link rel="manifest" href="<?php echo esc_attr( 'data:application/manifest+json;base64,' . base64_encode( wp_json_encode( $manifest ) ) ); ?>">
<?php
}
);
@westonruter
Copy link
Author

To avoid the use of the data: URL (which can apparently not always be ideal), there is this alternative. Replace the admin_head action callback above with:

use WP_Web_App_Manifest;
const SCOPE_QUERY_VAR = 'scope';
const ADMIN_SCOPE = 'admin';

add_action(
	'admin_head',
	static function () {
		if ( ! method_exists( 'WP_Web_App_Manifest', 'get_url' ) ) {
			// Bail if PWA plugin is not active or the class name changed in core merge.
			return;
		}

		$url = add_query_arg(
			array(
				// The scope query arg here is added to customize the output of the web_app_manifest filter below.
				SCOPE_QUERY_VAR => ADMIN_SCOPE,
				// The nonce allows for the web-app-manifest to have access to the user's authentication state.
				// This must be paired with the crossorigin=use-credentials attribute on the link[rel=manifest] tag.
				'_wpnonce' => wp_create_nonce( 'wp_rest' ),
			),
			WP_Web_App_Manifest::get_url()
		);

		?>
		<link rel="manifest" href="<?php echo esc_url( $url ); ?>" crossorigin="use-credentials">
		<?php
	}
);


add_filter(
	'web_app_manifest',
	static function ( array $manifest ): array {
		if ( isset( $_GET[ SCOPE_QUERY_VAR ] ) && ADMIN_SCOPE === $_GET[ SCOPE_QUERY_VAR ] ) {
			$manifest = array_merge( $manifest, get_admin_web_app_manifest_overrides() );
		}
		return $manifest;
	}
);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment