Update plugin from personal API

I’m developing a wordpress plugin at the moment which I do not want in the WordPress plugin repository. However I still want to be able to push updates to my costumers from my own API repository.

I’ve been reading quite a bit about this, and one thing that seems to be something about is the pre_set_site_transient_update_plugins filter, however I can’t find much info about this. I’ve tried this tutorial (http://konstruktors.com/blog/wordpress/2538-automatic-updates-for-plugins-and-themes-hosted-outside-wordpress-extend/) which I couldn’t get working. I can tell from the comments that others can actually get this working with what must be almost the current version of WP (latest response Apr 22).

Read More

I tried installing the plugin from the site and putting the API folder on a second domain, but the update notification I usually get when an update is available, didn’t show anywhere at all.

I’m not sure if it is actually possible to have custom plugins run the auto-update from other repositories so I would like to hear if anyone in here has any experience at all with this stuff? The solution in the tutorial seemed to be an easy solution – I wonder if it’s somehow possible to do it in a more advanced way?

Any help getting this auto-update from my own repository working would be much appreciated!

(PS: I’m running WP version 3.1.3)

Related posts

Leave a Reply

5 comments

  1. For a single-site install (I haven’t tested it on a multi-site), there are only two hooks you need to update from an external service such as github or gitlab. In the code below, I use gitlab since that’s what I use to host my code right now. I probably should abstract the gitlab parts out…

    The first hook you’ll need to use is pre_set_site_transient_update_themes. This is the filter that WordPress uses to set the site_transient to show if there are updates available. Use this hook to connect to your remote version and see if there are updates available. If there are, then modify the transient so that WordPress knows there are updates and can show the notice to the user.

    The other hook you’ll need to use is upgrader_source_selection. This filter is needed, for gitlab anyway, because the name of the downloaded folder is not the same as the theme, so we use this hook to rename it to the correct name. If you’re remote repository provides a zip with the correct name, then you don’t even need this hook.

    The third, optional, hook you can use is auto_update_theme if you want to auto update your theme. In the example below, I use this hook to auto update only this specific theme.

    This code has only been tested with WordPress 4.9.x. It requires PHP > 7.0.

    functions.php

    //* Load the updater.
    require PATH_TO . 'updater.php';
    $updater = new updater();
    add_action( 'init', [ $updater, 'init' ] );
    

    updater.php

    /**
     * @package StackExchangeWordPress
     */
    declare( strict_types = 1 );
    namespace StackExchangeWordPress;
    
    /**
     * Class for updating the theme.
     */
    class updater {
    
      /**
       * @var Theme slug.
       */
      protected $theme = 'theme';
    
      /**
       * @var Theme repository name.
       */
      protected $repository = 'project/theme';
    
      /**
       * @var Repository domain.
       */
      protected $domain = 'https://gitlab.com/';
    
      /**
       * @var CSS endpoint for repository.
       */
      protected $css_endpoint = '/raw/master/style.css';
    
      /**
       * @var ZIP endpoint for repository.
       */
      protected $zip_endpoint = '/repository/archive.zip';
    
      /**
       * @var Remote CSS URI.
       */
      protected $remote_css_uri;
    
      /**
       * @var Remote ZIP URI.
       */
      protected $remote_zip_uri;
    
      /**
       * @var Remote version.
       */
      protected $remote_version;
    
      /**
       * @var Local version.
       */
      protected $local_version;
    
      /**
       * Method called from the init hook to initiate the updater
       */
      public function init() {
        add_filter( 'auto_update_theme', [ $this, 'auto_update_theme' ], 20, 2 );
        add_filter( 'upgrader_source_selection', [ $this, 'upgrader_source_selection' ], 10, 4 );
        add_filter( 'pre_set_site_transient_update_themes', [ $this, 'pre_set_site_transient_update_themes' ] );
      }
    
      /**
       * Method called from the auto_update_theme hook.
       * Only auto update this theme.
       * This hook and method are only needed if you want to auto update the theme.
       *
       * @return bool Whether to update the theme.
       */
      public function auto_update_theme( bool $update, stdClass $item ) : bool {
        return $this->theme === $item->theme;
      }
    
      /**
       * Rename the unzipped folder to be the same as the existing folder
       *
       * @param string       $source        File source location
       * @param string       $remote_source Remote file source location
       * @param WP_Upgrader $upgrader      WP_Upgrader instance
       * @param array        $hook_extra    Extra arguments passed to hooked filters
       *
       * @return string | WP_Error The updated source location or a WP_Error object on failure
       */
      public function upgrader_source_selection( string $source, string $remote_source, WP_Upgrader $upgrader, array $hook_extra ) {
        global $wp_filesystem;
    
        $update = [ 'update-selected', 'update-selected-themes', 'upgrade-theme' ];
    
        if( ! isset( $_GET[ 'action' ] ) || ! in_array( $_GET[ 'action' ], $update, true ) ) {
          return $source;
        }
    
        if( ! isset( $source, $remote_source ) ) {
          return $source;
        }
    
        if( false === stristr( basename( $source ), $this->theme ) ) {
          return $source;
        }
    
        $basename = basename( $source );
        $upgrader->skin->feedback( esc_html_e( 'Renaming theme directory.', 'bootstrap' ) );
        $corrected_source = str_replace( $basename, $this->theme, $source );
    
        if( $wp_filesystem->move( $source, $corrected_source, true ) ) {
          $upgrader->skin->feedback( esc_html_e( 'Rename successful.', 'bootstrap' ) );
          return $corrected_source;
        }
    
        return new WP_Error();
      }
    
      /**
       * Add respoinse to update transient if theme has an update.
       *
       * @param $transient
       *
       * @return
       */
      public function pre_set_site_transient_update_themes( $transient ) {
        require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
        $this->local_version = ( wp_get_theme( $this->theme ) )->get( 'Version' );
    
        if( $this->hasUpdate() ) {
          $response = [
            'theme'       => $this->theme,
            'new_version' => $this->remote_version,
            'url'         => $this->construct_repository_uri(),
            'package'     => $this->construct_remote_zip_uri(),
            'branch'      => 'master',
          ];
          $transient->response[ $this->theme ] = $response;
        }
    
        return $transient;
      }
    
      /**
       * Construct and return the URI to the remote stylesheet
       *
       * @return string The remote stylesheet URI
       */
      protected function construct_remote_stylesheet_uri() : string {
        return $this->remote_css_uri = $this->domain . $this->repository . $this->css_endpoint;
      }
    
      /**
       * Construct and return the URI to the remote ZIP file
       *
       * @return string The remote ZIP URI
       */
      protected function construct_remote_zip_uri() : string {
        return $this->remote_zip_uri = $this->domain . $this->repository . $this->zip_endpoint;
      }
    
      /**
       * Construct and return the URI to remote repository
       *
       * @access protected
       * @since  1.0
       *
       * @return string The remote repository URI
       */
      protected function construct_repository_uri() : string {
        return $this->repository_uri = $this->domain . trailingslashit( $this->repository );
      }
    
      /**
       * Get and return the remote version
       *
       * @return string The remote version
       */
      protected function get_remote_version() : string {
        $this->remote_stylesheet_uri = $this->construct_remote_stylesheet_uri();
        $response = $this->remote_get( $this->remote_stylesheet_uri );
        $response = str_replace( "r", "n", wp_remote_retrieve_body( $response ) );
        $headers = [ 'Version' => 'Version' ];
    
        foreach( $headers as $field => $regex ) {
          if( preg_match( '/^[ t/*#@]*' . preg_quote( $regex, '/' ) . ':(.*)$/mi', $response, $match ) && $match[1] ) {
            $headers[ $field ] = _cleanup_header_comment( $match[1] );
          }
          else {
            $headers[ $field ] = '';
          }
        }
    
        return $this->remote_version = ( '' === $headers[ 'Version' ] ) ? '' : $headers[ 'Version' ];
      }
    
      /**
       * Return whether the theme has an update
       *
       * @return bool Whether the theme has an update
       */
      protected function hasUpdate() : bool {
        if( ! $this->remote_version ) $this->remote_version = $this->get_remote_version();
        return version_compare( $this->remote_version, $this->local_version, '>' );
      }
    
      /**
       * Wrapper for wp_remote_get()
       *
       * @param string $url  The URL to get
       * @param array  $args Array or arguments to pass through to wp_remote_get()
       *
       * @return array|WP_Error Return the request or an error object
       */
      protected function remote_get( string $url, array $args = [] ) {
        return wp_remote_get( $url, $args );
      }
    }
    
  2. There is this commercial Plugin and Theme Update API manager for WooCommerce that specifically works if the plugin or theme is not hosted on wordpress.org. It is designed to provide updates for self hosted plugins and themes. The plugin is for those who don’t want to write it yourself, and need a lot of features, plus working examples for plugins and themes that are being sold.

    http://www.toddlahman.com/shop/wordpress-automatic-update-api-manager/