diff --git a/README.md b/README.md
index ce9cd35..22b3555 100644
--- a/README.md
+++ b/README.md
@@ -28,7 +28,7 @@ Adding the `mediaPosition` attribute will enable the Media Position toggle butto
* Root level transformations will apply to all child blocks.
* `reverse` will reverse the order of the child blocks.
* `attributes` will modify the block's attributes.
- * Each value will be determined based on the value of `mediaPosition`. See example below.
+ * Each value will be determined based on the value of `mediaPosition`. See example below.
* Nested `innerBlocks` will apply transformations only to child blocks when present within the parent block.
In the following example scenario:
@@ -171,7 +171,7 @@ vgtbt()->block_icons->get_icon( 'my-custom-icon' );
Outputs the block attributes as a string. Supported arguments:
* `$block` array - The block data.
-* `$custom_class` string - Additional classes to add to the block.
+* `$custom_class` string - Additional classes to add to the block.
* `$attrs` array - Additional attributes to add to the block.
### `inner_blocks()`
@@ -214,7 +214,7 @@ add_filter(
'icon' => '',
'defaultLeft' => false, // Optional, defaults icon to align left.
];
-
+
return $icons;
}
);
@@ -282,3 +282,7 @@ add_filter(
}
);
```
+
+## Auto-Updates
+
+This plugin automatically checks for updates from GitHub releases every 12 hours and can be updated directly from the WordPress dashboard.
diff --git a/includes/updater.php b/includes/updater.php
new file mode 100644
index 0000000..3773d06
--- /dev/null
+++ b/includes/updater.php
@@ -0,0 +1,293 @@
+plugin_file = $plugin_file;
+ $this->github_owner = $github_owner;
+ $this->github_repo = $github_repo;
+ $this->plugin_basename = plugin_basename( $plugin_file );
+
+ // Get plugin data.
+ $plugin_data = get_plugin_data( $plugin_file );
+ $this->plugin_slug = dirname( $this->plugin_basename );
+ $this->plugin_version = $plugin_data['Version'];
+
+ // Hook into WordPress.
+ add_filter( 'pre_set_site_transient_update_plugins', [ $this, 'check_for_update' ] );
+ add_filter( 'plugins_api', [ $this, 'plugin_info' ], 10, 3 );
+ add_action( 'upgrader_process_complete', [ $this, 'purge_cache' ], 10, 2 );
+ }
+ );
+ }
+
+ /**
+ * Check for plugin updates.
+ *
+ * @param object $transient Update transient object.
+ *
+ * @return object Modified transient object.
+ */
+ public function check_for_update( $transient ) {
+ if ( empty( $transient->checked ) ) {
+ return $transient;
+ }
+
+ $release_info = $this->get_release_info();
+
+ if ( ! $release_info || ! isset( $release_info->tag_name ) ) {
+ return $transient;
+ }
+
+ // Remove 'v' prefix from tag name for version comparison.
+ $latest_version = ltrim( $release_info->tag_name, 'v' );
+
+ // Check if there's a newer version.
+ if ( version_compare( $this->plugin_version, $latest_version, '<' ) ) {
+ $package_url = $this->get_package_url( $release_info );
+
+ if ( $package_url ) {
+ $transient->response[ $this->plugin_basename ] = (object) [
+ 'slug' => $this->plugin_slug,
+ 'plugin' => $this->plugin_basename,
+ 'new_version' => $latest_version,
+ 'url' => $release_info->html_url,
+ 'package' => $package_url,
+ 'icons' => [],
+ 'banners' => [],
+ 'banners_rtl' => [],
+ 'tested' => '',
+ 'requires_php' => '',
+ ];
+ }
+ }
+
+ return $transient;
+ }
+
+ /**
+ * Provide plugin information for the update details modal
+ *
+ * @param false|object|array $result The result object or array.
+ * @param string $action The type of information being requested.
+ * @param object $args Plugin API arguments.
+ *
+ * @return false|object Plugin information or false.
+ */
+ public function plugin_info( $result, $action, $args ) {
+ if ( 'plugin_information' !== $action ) {
+ return $result;
+ }
+
+ if ( ! isset( $args->slug ) || $args->slug !== $this->plugin_slug ) {
+ return $result;
+ }
+
+ $release_info = $this->get_release_info();
+
+ if ( ! $release_info ) {
+ return $result;
+ }
+
+ $latest_version = ltrim( $release_info->tag_name, 'v' );
+
+ return (object) [
+ 'name' => $this->plugin_slug,
+ 'slug' => $this->plugin_slug,
+ 'version' => $latest_version,
+ 'author' => $release_info->author->login,
+ 'author_profile' => $release_info->author->html_url,
+ 'last_updated' => $release_info->published_at,
+ 'homepage' => $release_info->html_url,
+ 'short_description' => esc_html__( 'Latest version from GitHub', 'viget-blocks-toolkit' ),
+ 'sections' => [
+ 'changelog' => $this->format_changelog( $release_info->body ),
+ ],
+ 'download_link' => $this->get_package_url( $release_info ),
+ 'requires' => '',
+ 'tested' => '',
+ 'requires_php' => '',
+ ];
+ }
+
+ /**
+ * Clear cache after successful update
+ *
+ * @param \WP_Upgrader $upgrader Upgrader instance.
+ * @param array $options Update options.
+ */
+ public function purge_cache( $upgrader, $options ) {
+ if ( 'update' !== $options['action'] || 'plugin' !== $options['type'] ) {
+ return;
+ }
+
+ if ( empty( $options['plugins'] ) || ! in_array( $this->plugin_basename, $options['plugins'], true ) ) {
+ return;
+ }
+
+ delete_site_transient( $this->get_transient_key() );
+ }
+
+ /**
+ * Get release information from GitHub API.
+ *
+ * @return object|false Release information or false on failure.
+ */
+ private function get_release_info() {
+ $transient_key = $this->get_transient_key();
+ $release_info = get_site_transient( $transient_key );
+
+ if ( false === $release_info ) {
+ $api_url = sprintf(
+ 'https://api.github.com/repos/%s/%s/releases/latest',
+ $this->github_owner,
+ $this->github_repo
+ );
+
+ $response = wp_remote_get(
+ $api_url,
+ [
+ 'timeout' => 10,
+ 'headers' => [
+ 'Accept' => 'application/vnd.github.v3+json',
+ 'User-Agent' => 'WordPress-Plugin-Updater',
+ ],
+ ]
+ );
+
+ if ( is_wp_error( $response ) ) {
+ return false;
+ }
+
+ $response_code = wp_remote_retrieve_response_code( $response );
+ if ( 200 !== $response_code ) {
+ return false;
+ }
+
+ $release_info = json_decode( wp_remote_retrieve_body( $response ) );
+
+ if ( ! $release_info ) {
+ return false;
+ }
+
+ // Cache for 12 hours.
+ set_site_transient( $transient_key, $release_info, 12 * HOUR_IN_SECONDS );
+ }
+
+ return $release_info;
+ }
+
+ /**
+ * Get download URL for the release package.
+ *
+ * @param object $release_info Release information from GitHub API.
+ *
+ * @return string|false Download URL or false if not found.
+ */
+ private function get_package_url( $release_info ): string|false {
+ if ( ! isset( $release_info->assets ) || ! is_array( $release_info->assets ) ) {
+ return false;
+ }
+
+ // Look for a ZIP file asset.
+ foreach ( $release_info->assets as $asset ) {
+ if ( ! empty( $asset->content_type ) && in_array( $asset->content_type, [ 'application/zip', 'application/octet-stream' ], true ) ) {
+ return $asset->browser_download_url;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Format changelog text.
+ *
+ * @param string $changelog Raw changelog text.
+ * @return string Formatted changelog.
+ */
+ private function format_changelog( $changelog ) {
+ if ( empty( $changelog ) ) {
+ return esc_html__( 'No changelog available.', 'viget-blocks-toolkit' );
+ }
+
+ // Convert markdown links to HTML
+ $changelog = preg_replace( '/\[([^\]]+)\]\(([^)]+)\)/', '$1', $changelog );
+
+ // Convert line breaks to HTML
+ $changelog = nl2br( esc_html( $changelog ) );
+
+ return $changelog;
+ }
+
+ /**
+ * Get transient key for caching
+ *
+ * @return string Transient key.
+ */
+ private function get_transient_key() {
+ return 'github_updater_' . md5( $this->github_owner . '/' . $this->github_repo );
+ }
+}
diff --git a/src/classes/Core.php b/src/classes/Core.php
index 9a00e11..e984323 100644
--- a/src/classes/Core.php
+++ b/src/classes/Core.php
@@ -39,6 +39,13 @@ class Core {
public function __construct() {
$this->block_icons = new BlockIcons();
$this->bp_visibility = new BreakpointVisibility();
+
+ // Initialize GitHub updater
+ new \GitHub_Plugin_Updater(
+ VGTBT_PLUGIN_FILE,
+ 'vigetlabs',
+ 'viget-blocks-toolkit'
+ );
}
/**
diff --git a/viget-blocks-toolkit.php b/viget-blocks-toolkit.php
index 4a59123..3d210ad 100644
--- a/viget-blocks-toolkit.php
+++ b/viget-blocks-toolkit.php
@@ -19,15 +19,21 @@
// Plugin version.
const VGTBT_VERSION = '1.1.1';
+// Plugin file.
+define( 'VGTBT_PLUGIN_FILE', __FILE__ );
+
// Plugin path.
-define( 'VGTBT_PLUGIN_PATH', plugin_dir_path( __FILE__ ) );
+define( 'VGTBT_PLUGIN_PATH', plugin_dir_path( VGTBT_PLUGIN_FILE ) );
// Plugin URL.
-define( 'VGTBT_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
+define( 'VGTBT_PLUGIN_URL', plugin_dir_url( VGTBT_PLUGIN_FILE ) );
// Helper functions.
require_once 'includes/helpers.php';
+// Plugin updater.
+require_once 'includes/updater.php';
+
// Timber functions.
require_once 'includes/timber.php';