├── LICENSE ├── README.md ├── Updater.php ├── _config.yml └── composer.json /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Aristeides Stathopoulos 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # github-theme-updater 2 | 3 | This is a simple class that lets you use github releases in your repository to handle your theme updates. 4 | A more robust solution would be to use the [github-updater](https://github.com/afragen/github-updater) plugin, but I simply refuse to include a 540kb library in my theme when I can accomplish what I need in less than 200 lines of code. 5 | Is it perfect? No. Does it get the job done? Yes. 6 | 7 | ## Implementing in the theme: 8 | 9 | 1. Download the php file from this repo and include it in your theme, or require it using composer: 10 | ```bash 11 | composer require aristath/github-theme-updater 12 | ``` 13 | 2. Change the namespace on the top of the file to match the one you use in your theme (you are using namespaces, right?). 14 | 3. Include the updater class in your functions.php file: 15 | ```php 16 | add_action( 'after_setup_theme', function() { 17 | get_template_part( 'inc/classes/Updater' ); 18 | }); 19 | ``` 20 | 4. At the bottom of the `Updater.php` file init the updater: 21 | ```php 22 | new Updater( 23 | [ 24 | 'name' => 'Gridd', // Theme Name. 25 | 'repo' => 'wplemon/gridd', // Theme repository. 26 | 'slug' => 'gridd', // Theme Slug. 27 | 'url' => 'https://wplemon.com/gridd', // Theme URL. 28 | 'ver' => 1.2 // Theme Version. 29 | ] 30 | ); 31 | ``` 32 | 33 | ## Releasing an update on Github 34 | 35 | When you want to release an update you can simply go to your repository > Releases and create a new release. 36 | * The version should either be formatted as `1.2`, `1.2.3`, `v1.2` or `v1.2.3` etc. The version comparison will remove the `v` and the script will be able to compare versions. However, if you call your release-tag `tomato` don't expect it to work. 37 | * You **have to upload a zip file.**. When creating your release, upload a `.zip` file in there. The default packages created by github have the version number inside the folder-name, and that has the potential to break things on user sites when updating. **This updater script will only detect the file uploaded as an asset manually by you**. 38 | 39 | ## Advice 40 | 41 | * Use at your own risk. If you don't follow the instructions above, things will break. 42 | * This script is as simple as it can be. If you find a bug, pull-requests are more than welcomed. 43 | 44 | ## Why was this created? 45 | 46 | This project was created out of necessity for a theme submitted on wordpress.org. The theme-review process on wordpress.org takes a long time... 47 | 48 | * The initial review is usually a coupe of months. 49 | * If all goes well the theme proceeds to the final review which takes 2-4 months at the time of this writing. 50 | * If your theme is tagged as accessibility-ready, then the theme has to wait for a 3rd review, and I currently have no idea how long that will take. 51 | 52 | Overall a theme review can easily take more than 6 months. 53 | 54 | In the meantime, life goes on. If your business depends on your themes, then you may want to implement an alternative method to provide updates for your theme so people can start using it and you can start growing. 55 | 56 | ## Releasing your theme on w.org 57 | 58 | Remember to remove the script when uploading an update on w.org. When your theme goes live you can remove it from the repository as well, this is only meant as a stepping stone. 59 | 60 | Until your theme goes live you can ignore the class inclusion if you did it using `get_template_part` as in the example above. If the file doesn't exist nothing bad will happen. 61 | 62 | Personally I prefer using a `.gitattributes` file to exclude the development files like `.sass`, `.map`, `.editorconfig` etc. If you use such a file on your project, you can just add `Updater.php export-ignore` in there and it won't be included in your theme export when you get your build ready for w.org 63 | 64 | That's all. I hope you enjoy using this and it makes your life somewhat easier. -------------------------------------------------------------------------------- /Updater.php: -------------------------------------------------------------------------------- 1 | name = $args['name']; 82 | $this->slug = $args['slug']; 83 | $this->repo = $args['repo']; 84 | $this->ver = $args['ver']; 85 | $this->url = $args['url']; 86 | 87 | $this->response = $this->get_response(); 88 | // Check for theme updates. 89 | add_filter( 'http_request_args', [ $this, 'update_check' ], 5, 2 ); 90 | // Inject theme updates into the response array. 91 | add_filter( 'pre_set_site_transient_update_themes', [ $this, 'update_themes' ] ); 92 | add_filter( 'pre_set_transient_update_themes', [ $this, 'update_themes' ] ); 93 | } 94 | 95 | /** 96 | * Gets the releases URL. 97 | * 98 | * @access private 99 | * @since 1.0 100 | * @return string 101 | */ 102 | private function get_releases_url() { 103 | return 'https://api.github.com/repos/' . $this->repo . '/releases'; 104 | } 105 | 106 | /** 107 | * Get the response from the Github API. 108 | * 109 | * @access private 110 | * @since 1.0 111 | * @return array 112 | */ 113 | private function get_response() { 114 | // Check transient. 115 | $cache = get_site_transient( md5( $this->get_releases_url() ) ); 116 | if ( $cache ) { 117 | return $cache; 118 | } 119 | $response = wp_remote_get( $this->get_releases_url() ); 120 | if ( ! is_wp_error( $response ) && 200 === wp_remote_retrieve_response_code( $response ) ) { 121 | $response = json_decode( wp_remote_retrieve_body( $response ), true ); 122 | set_site_transient( md5( $this->get_releases_url() ), $response, 12 * HOUR_IN_SECONDS ); 123 | } 124 | } 125 | 126 | /** 127 | * Get the new version file. 128 | * 129 | * @access private 130 | * @since 1.0 131 | * @return string 132 | */ 133 | private function get_latest_package() { 134 | if ( ! $this->response ) { 135 | return; 136 | } 137 | foreach ( $this->response as $release ) { 138 | if ( isset( $release['assets'] ) && isset( $release['assets'][0] ) && isset( $release['assets'][0]['browser_download_url'] ) ) { 139 | return $release['assets'][0]['browser_download_url']; 140 | } 141 | } 142 | } 143 | 144 | /** 145 | * Get the new version. 146 | * 147 | * @access private 148 | * @since 1.0 149 | * @return string 150 | */ 151 | private function get_latest_version() { 152 | if ( ! $this->response ) { 153 | return; 154 | } 155 | foreach ( $this->response as $release ) { 156 | if ( isset( $release['tag_name'] ) ) { 157 | return str_replace( 'v', '', $release['tag_name'] ); 158 | } 159 | } 160 | } 161 | 162 | /** 163 | * Disables requests to the wp.org repository for this theme. 164 | * 165 | * @since 1.0 166 | * 167 | * @param array $request An array of HTTP request arguments. 168 | * @param string $url The request URL. 169 | * @return array 170 | */ 171 | public function update_check( $request, $url ) { 172 | if ( false !== strpos( $url, '//api.wordpress.org/themes/update-check/1.1/' ) ) { 173 | $data = json_decode( $request['body']['themes'] ); 174 | unset( $data->themes->{$this->slug} ); 175 | $request['body']['themes'] = wp_json_encode( $data ); 176 | } 177 | return $request; 178 | } 179 | 180 | /** 181 | * Inject update data for this theme. 182 | * 183 | * @since 1.0 184 | * 185 | * @param object $transient The pre-saved value of the `update_themes` site transient. 186 | * @return object 187 | */ 188 | public function update_themes( $transient ) { 189 | if ( isset( $transient->checked ) ) { 190 | $current_version = $this->ver; 191 | 192 | if ( version_compare( $current_version, $this->get_latest_version(), '<' ) ) { 193 | $transient->response[ $this->name ] = [ 194 | 'theme' => $this->name, 195 | 'new_version' => $this->get_latest_version(), 196 | 'url' => 'https://github.com/' . $this->repo . '/releases', 197 | 'package' => $this->get_latest_package(), 198 | ]; 199 | } 200 | } 201 | return $transient; 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aristath/github-theme-updater", 3 | "type": "library", 4 | "keywords": ["WordPress", "Theme Updater", "Github"], 5 | "description": "Custom updater class for WordPress themes hosted on Github", 6 | "homepage": "https://aristath.github.io/github-theme-updater", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "aristath", 11 | "email": "aristath@gmail.com", 12 | "homepage": "https://aristath.github.io", 13 | "role":"Developer" 14 | } 15 | ], 16 | "require": { 17 | "php": ">=5.6" 18 | } 19 | } 20 | --------------------------------------------------------------------------------