├── samples
├── edd-sample-theme
│ ├── screenshot.png
│ ├── README.md
│ ├── wp-dependencies.json
│ ├── index.php
│ ├── style.css
│ └── functions.php
└── edd-sample-plugin
│ ├── README.md
│ ├── wp-dependencies.json
│ └── edd-sample-plugin.php
├── vendor
├── autoload.php
├── composer
│ ├── autoload_namespaces.php
│ ├── autoload_classmap.php
│ ├── autoload_psr4.php
│ ├── installed.php
│ ├── platform_check.php
│ ├── LICENSE
│ ├── autoload_static.php
│ ├── autoload_real.php
│ ├── installed.json
│ ├── InstalledVersions.php
│ └── ClassLoader.php
└── afragen
│ └── translations-updater
│ ├── CHANGES.md
│ ├── LICENSE
│ ├── composer.json
│ ├── src
│ └── Translations_Updater
│ │ ├── Init.php
│ │ ├── Language_Pack.php
│ │ ├── Base.php
│ │ ├── API.php
│ │ └── Language_Pack_API.php
│ └── README.md
├── edd-sl-updater.php
├── LICENSE
├── composer.json
├── src
├── Bootstrap.php
├── Init.php
├── Theme_Updater.php
├── License_Form.php
├── Plugin_Updater.php
├── Theme_Updater_Admin.php
├── Plugin_Updater_Admin.php
├── Settings.php
├── Updater_Common.php
├── License_Actions.php
└── API_Common.php
├── CHANGES.md
├── languages
└── edd-sl-updater.pot
├── readme.txt
└── README.md
/samples/edd-sample-theme/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afragen/edd-sl-updater/develop/samples/edd-sample-theme/screenshot.png
--------------------------------------------------------------------------------
/samples/edd-sample-plugin/README.md:
--------------------------------------------------------------------------------
1 | To correctly utilize this sample. You must run `composer install afragen/wp-dependency-installer` from the sample's directory.
2 |
--------------------------------------------------------------------------------
/samples/edd-sample-theme/README.md:
--------------------------------------------------------------------------------
1 | To correctly utilize this sample. You must run `composer install afragen/wp-dependency-installer` from the sample's directory.
2 |
--------------------------------------------------------------------------------
/vendor/autoload.php:
--------------------------------------------------------------------------------
1 | $vendorDir . '/composer/InstalledVersions.php',
10 | );
11 |
--------------------------------------------------------------------------------
/vendor/composer/autoload_psr4.php:
--------------------------------------------------------------------------------
1 | array($vendorDir . '/afragen/translations-updater/src/Translations_Updater'),
10 | 'EDD\\Software_Licensing\\Updater\\' => array($baseDir . '/src'),
11 | );
12 |
--------------------------------------------------------------------------------
/vendor/afragen/translations-updater/CHANGES.md:
--------------------------------------------------------------------------------
1 | #### [unreleased]
2 | * make work with self-hosted installs of git hosts
3 | * update `Init::can_update()` for parity with GitHub Updater
4 | * update for possible universal EDD SL Updater plugin
5 | * switch to `site_transient_update_{plugins|themes}` filter
6 | * convert to composer dependency from [EDD Translations Updater](https://github.com/afragen/edd-translations-updater) and make more generic for any WordPress plugin or theme
7 | * support EDD Software Licensing `post_edd_sl_{plugin|theme}_updater_setup` action hooks
8 | * update for Bitbucket API 2.0
9 | * initial commit
10 |
--------------------------------------------------------------------------------
/samples/edd-sample-theme/index.php:
--------------------------------------------------------------------------------
1 |
12 | >
13 |
14 |
15 | EDD Sample Theme
16 |
17 |
18 |
19 |
20 | >
21 |
22 |
23 |
24 | EDD Sample Theme
25 | Easy licensing for your WordPress Themes
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/samples/edd-sample-theme/style.css:
--------------------------------------------------------------------------------
1 | /*
2 | Theme Name: EDD Sample Theme
3 | Theme URI: http://pippinsplugins.com
4 | Description: An example theme to show how EDD licensing works
5 | Author: Pippin Williamson, Devin Price
6 | Author URI: http://pippinsplugins.com
7 | Version: 1.0
8 | License: GNU General Public License v2.0 or later
9 | License URI: http://www.gnu.org/licenses/gpl-2.0.html
10 | */
11 |
12 | body {
13 | margin:0;
14 | background:#eee;
15 | }
16 |
17 | body, input, textarea {
18 | color: #373737;
19 | font: 15px "Helvetica Neue", Helvetica, Arial, sans-serif;
20 | font-weight: 300;
21 | line-height: 1.625;
22 | }
23 |
24 | p {
25 | margin:0 0 10px 0;
26 | }
27 |
28 | h1, h2, h3 {
29 | margin:0 0 15px 0;
30 | }
31 |
32 | article {
33 | background:#000;
34 | color:#fff;
35 | margin:14% 0 0 6%;
36 | padding:2%;
37 | }
38 |
--------------------------------------------------------------------------------
/vendor/composer/installed.php:
--------------------------------------------------------------------------------
1 |
3 | array (
4 | 'pretty_version' => 'dev-develop',
5 | 'version' => 'dev-develop',
6 | 'aliases' =>
7 | array (
8 | ),
9 | 'reference' => '3ce34e7b89473dda7c4fce4533a6761e3b37e058',
10 | 'name' => 'afragen/edd-sl-updater',
11 | ),
12 | 'versions' =>
13 | array (
14 | 'afragen/edd-sl-updater' =>
15 | array (
16 | 'pretty_version' => 'dev-develop',
17 | 'version' => 'dev-develop',
18 | 'aliases' =>
19 | array (
20 | ),
21 | 'reference' => '3ce34e7b89473dda7c4fce4533a6761e3b37e058',
22 | ),
23 | 'afragen/translations-updater' =>
24 | array (
25 | 'pretty_version' => 'dev-master',
26 | 'version' => 'dev-master',
27 | 'aliases' =>
28 | array (
29 | ),
30 | 'reference' => '93cd3ad14f59e435f0cfa915c6971126095a29f1',
31 | ),
32 | ),
33 | );
34 |
--------------------------------------------------------------------------------
/edd-sl-updater.php:
--------------------------------------------------------------------------------
1 | run();
34 |
--------------------------------------------------------------------------------
/vendor/composer/platform_check.php:
--------------------------------------------------------------------------------
1 | = 50600)) {
8 | $issues[] = 'Your Composer dependencies require a PHP version ">= 5.6.0". You are running ' . PHP_VERSION . '.';
9 | }
10 |
11 | if ($issues) {
12 | if (!headers_sent()) {
13 | header('HTTP/1.1 500 Internal Server Error');
14 | }
15 | if (!ini_get('display_errors')) {
16 | if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
17 | fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
18 | } elseif (!headers_sent()) {
19 | echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
20 | }
21 | }
22 | trigger_error(
23 | 'Composer detected issues in your platform: ' . implode(' ', $issues),
24 | E_USER_ERROR
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Andy Fragen
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 |
--------------------------------------------------------------------------------
/vendor/composer/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Copyright (c) Nils Adermann, Jordi Boggiano
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy
5 | of this software and associated documentation files (the "Software"), to deal
6 | in the Software without restriction, including without limitation the rights
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the Software is furnished
9 | to do so, subject to the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included in all
12 | copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | THE SOFTWARE.
21 |
22 |
--------------------------------------------------------------------------------
/vendor/afragen/translations-updater/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Andy Fragen
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 |
--------------------------------------------------------------------------------
/vendor/afragen/translations-updater/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "afragen/translations-updater",
3 | "description": "This framework provides automatic decoupled languate pack updates from a public repository for your WordPress plugin or theme.",
4 | "type": "library",
5 | "license": "MIT",
6 | "keywords": [
7 | "wordpress",
8 | "plugin",
9 | "theme",
10 | "updater",
11 | "language pack",
12 | "translations"
13 | ],
14 | "authors": [
15 | {
16 | "name": "Andy Fragen",
17 | "email": "andy@thefragens.com",
18 | "homepage": "https://thefragens.com",
19 | "role": "Developer"
20 | }
21 | ],
22 | "support": {
23 | "issues": "https://github.com/afragen/translations-updater/issues",
24 | "source": "https://github.com/afragen/translations-updater"
25 | },
26 | "repositories": [
27 | {
28 | "type": "vcs",
29 | "url": "https://github.com/afragen/translations-updater"
30 | }
31 | ],
32 | "prefer-stable": true,
33 | "require": {
34 | "php": ">=5.4"
35 | },
36 | "autoload": {
37 | "psr-4": {
38 | "Fragen\\Translations_Updater\\": "src/Translations_Updater/"
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "afragen/edd-sl-updater",
3 | "description": "A universal updater for EDD Software Licensing.",
4 | "type": "wordpress-plugin",
5 | "keywords": [
6 | "easydigitaldownloads",
7 | "software licensing",
8 | "updater"
9 | ],
10 | "license": "MIT",
11 | "authors": [
12 | {
13 | "name": "Andy Fragen",
14 | "email": "andy@thefragens.com",
15 | "homepage": "https://thefragens.com",
16 | "role": "Developer"
17 | },
18 | {
19 | "name": "Easy Digital Downloads",
20 | "homepage": "https://easydigitaldownloads.com",
21 | "role": "Developer"
22 | }
23 | ],
24 | "repositories": [
25 | {
26 | "type": "vcs",
27 | "url": "https://github.com/afragen/edd-sl-updater"
28 | }
29 | ],
30 | "support": {
31 | "issues": "https://github.com/afragen/edd-sl-updater/issues",
32 | "source": "https://github.com/afragen/edd-sl-updater"
33 | },
34 | "prefer-stable": true,
35 | "require": {
36 | "php": ">=5.6",
37 | "afragen/translations-updater": "dev-master"
38 | },
39 | "autoload": {
40 | "psr-4": {
41 | "EDD\\Software_Licensing\\Updater\\": "src/"
42 | }
43 | },
44 | "scripts": {
45 | "post-update-cmd": [
46 | "wp i18n make-pot . languages/edd-sl-updater.pot"
47 | ]
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/samples/edd-sample-plugin/edd-sample-plugin.php:
--------------------------------------------------------------------------------
1 | run();
13 |
14 | /**
15 | * Load updater.
16 | * Must be in main plugin file.
17 | *
18 | * @return void
19 | */
20 | function edd_test_plugin_updater() {
21 | $config = [
22 | 'type' => 'plugin', // Declare the type.
23 | 'file' => __FILE__,
24 | 'api_url' => 'http://eddstore.test', // Site where EDD SL store is located.
25 | 'item_name' => 'EDD Test Plugin', // Name of plugin.
26 | 'item_id' => 11, // ID of the product.
27 | 'version' => '1.0', // Current version number.
28 | 'author' => 'Andy Fragen', // Author of this plugin.
29 | 'beta' => false,
30 | 'license' => '', // Optional, if plugin handles license actions you can set license here.
31 | ];
32 | if ( class_exists( 'EDD\\Software_Licensing\\Updater\\Bootstrap' ) ) {
33 | ( new EDD\Software_Licensing\Updater\Init() )->run( $config );
34 | }
35 | }
36 | add_action( 'plugins_loaded', 'edd_test_plugin_updater' );
37 |
--------------------------------------------------------------------------------
/src/Bootstrap.php:
--------------------------------------------------------------------------------
1 | file = $file;
35 | }
36 |
37 | /**
38 | * Let's get started.
39 | *
40 | * @return void
41 | */
42 | public function run() {
43 | add_action(
44 | 'init',
45 | function () {
46 | load_plugin_textdomain( 'edd-sl-updater' );
47 | }
48 | );
49 |
50 | // Initiate decoupled language pack updating.
51 | ( new \Fragen\Translations_Updater\Init( __NAMESPACE__ ) )->edd_run();
52 |
53 | $updater_config = [
54 | 'type' => 'plugin',
55 | 'file' => $this->file,
56 | 'api_url' => 'http://easydigitaldownloads.com',
57 | 'item_name' => 'EDD SL Updater',
58 | 'item_id' => 123,
59 | 'version' => '1.0',
60 | 'author' => 'Easy Digital Downloads',
61 | 'beta' => false,
62 | ];
63 | // ( new Init() )->run( $updater_config );
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/vendor/composer/autoload_static.php:
--------------------------------------------------------------------------------
1 |
11 | array (
12 | 'Fragen\\Translations_Updater\\' => 28,
13 | ),
14 | 'E' =>
15 | array (
16 | 'EDD\\Software_Licensing\\Updater\\' => 31,
17 | ),
18 | );
19 |
20 | public static $prefixDirsPsr4 = array (
21 | 'Fragen\\Translations_Updater\\' =>
22 | array (
23 | 0 => __DIR__ . '/..' . '/afragen/translations-updater/src/Translations_Updater',
24 | ),
25 | 'EDD\\Software_Licensing\\Updater\\' =>
26 | array (
27 | 0 => __DIR__ . '/../..' . '/src',
28 | ),
29 | );
30 |
31 | public static $classMap = array (
32 | 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
33 | );
34 |
35 | public static function getInitializer(ClassLoader $loader)
36 | {
37 | return \Closure::bind(function () use ($loader) {
38 | $loader->prefixLengthsPsr4 = ComposerStaticInit047415cf42b6d7dd064038c532564bfd::$prefixLengthsPsr4;
39 | $loader->prefixDirsPsr4 = ComposerStaticInit047415cf42b6d7dd064038c532564bfd::$prefixDirsPsr4;
40 | $loader->classMap = ComposerStaticInit047415cf42b6d7dd064038c532564bfd::$classMap;
41 |
42 | }, null, ClassLoader::class);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/samples/edd-sample-theme/functions.php:
--------------------------------------------------------------------------------
1 | run();
17 |
18 | // Required filter to ensure persist-admin-notices-dismissal properly loads JS.
19 | add_filter( 'pand_theme_loader', '__return_true' );
20 |
21 | /**
22 | * Test theme updater instantiate.
23 | *
24 | * @return void
25 | */
26 | function edd_test_theme_updater() {
27 | $config = [
28 | 'type' => 'theme', // Declare the type.
29 | 'api_url' => 'http://eddstore.test', // Site where EDD is hosted.
30 | 'item_name' => 'EDD Test Theme', // Name of theme.
31 | 'item_id' => 27, // Item ID from Downloads page.
32 | 'slug' => 'edd-test-theme', // Theme slug.
33 | 'version' => '1.0', // The current version of this theme.
34 | 'author' => 'Andy Fragen', // The author of this theme.
35 | 'download_id' => '', // Optional, used for generating a license renewal link.
36 | 'renew_url' => '', // Optional, allows for a custom license renewal link.
37 | 'beta' => false, // Optional, set to true to opt into beta versions.
38 | 'license' => '', // Optional, if theme handles license actions you can set license here.
39 | ];
40 | if ( class_exists( 'EDD\\Software_Licensing\\Updater\\Bootstrap' ) ) {
41 | ( new EDD\Software_Licensing\Updater\Init() )->run( $config );
42 | }
43 | }
44 | add_action( 'after_setup_theme', 'edd_test_theme_updater' );
45 |
--------------------------------------------------------------------------------
/CHANGES.md:
--------------------------------------------------------------------------------
1 | #### [unreleased]
2 | * initial commit
3 | * updated samples
4 | * consolidated strings
5 | * added `afragen/translations-updater` for decoupled language pack updating
6 | * added initialization and integration for `afragen/translations-updater`
7 | * updated samples to include test for class to prevent fatal when updating Updater
8 | * added `wp-dependency.json`, `composer require afragen/wp-dependency-installer`, and install code for samples to automatically install EDD SL Updater
9 | * created `class Init` with universal instantiator function, just add configuration array
10 | * created filter hook `edd_sl_license_form_table` to easily customize the license form
11 | * switch to `site_transient_update_{plugins|themes}` filter
12 | * created Settings page for licenses instead of appending to menus
13 | * added ability to set updater only using `Init->updater( $config )`
14 | * update examples for `afragen/wp-dependency-installer:^3`
15 | * sanitize, escape & ignore
16 | * remove some unnecessary function specific `$cache_key` references, [#1607](https://github.com/easydigitaldownloads/EDD-Software-Licensing/issues/1607)
17 | * add `update-available` to update transient so auto update link displays, WP 5.5
18 | * add plugin and theme data to `$transient->no_update` so auto update link displays
19 | * remove calling custom update meta row
20 | * plugin and theme updating via WP 5.5 auto-updating works, needed to bypass caps check during wp-cron 🤔
21 | * major refactor to combine common functions into `class Updater_Common` and `class API_Common`
22 | * move license check to `Updater_Common::update_transient()`
23 | * add `environment` param per [#1670](https://github.com/easydigitaldownloads/EDD-Software-Licensing/pull/1670/)
24 | * add an initiation for just using the license settings page.
25 |
--------------------------------------------------------------------------------
/src/Init.php:
--------------------------------------------------------------------------------
1 | load_hooks();
33 | if ( in_array( 'plugin', $config, true ) ) {
34 | ( new Plugin_Updater_Admin( $config ) )->load_hooks();
35 | }
36 | if ( in_array( 'theme', $config, true ) ) {
37 | ( new Theme_Updater_Admin( $config ) )->load_hooks();
38 | }
39 | }
40 |
41 | /**
42 | * Universal settings loader.
43 | *
44 | * Load the correct settings from a single function.
45 | *
46 | * @param array $config Configuration data for plugin or theme.
47 | *
48 | * @return void
49 | */
50 | public function no_updater( $config ) {
51 | ( new Settings() )->load_hooks();
52 | if ( in_array( 'plugin', $config, true ) ) {
53 | ( new Plugin_Updater_Admin( $config ) )->load_settings();
54 | }
55 | if ( in_array( 'theme', $config, true ) ) {
56 | ( new Theme_Updater_Admin( $config ) )->load_settings();
57 | }
58 | }
59 |
60 | /**
61 | * Universal updater.
62 | *
63 | * Load the correct updater from a single function.
64 | *
65 | * @param array $config Configuration data for plugin or theme.
66 | *
67 | * @return void
68 | */
69 | public function updater( $config ) {
70 | if ( in_array( 'plugin', $config, true ) ) {
71 | ( new Plugin_Updater_Admin( $config ) )->updater();
72 | }
73 | if ( in_array( 'theme', $config, true ) ) {
74 | ( new Theme_Updater_Admin( $config ) )->updater();
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/vendor/composer/autoload_real.php:
--------------------------------------------------------------------------------
1 | = 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
32 | if ($useStaticLoader) {
33 | require __DIR__ . '/autoload_static.php';
34 |
35 | call_user_func(\Composer\Autoload\ComposerStaticInit047415cf42b6d7dd064038c532564bfd::getInitializer($loader));
36 | } else {
37 | $map = require __DIR__ . '/autoload_namespaces.php';
38 | foreach ($map as $namespace => $path) {
39 | $loader->set($namespace, $path);
40 | }
41 |
42 | $map = require __DIR__ . '/autoload_psr4.php';
43 | foreach ($map as $namespace => $path) {
44 | $loader->setPsr4($namespace, $path);
45 | }
46 |
47 | $classMap = require __DIR__ . '/autoload_classmap.php';
48 | if ($classMap) {
49 | $loader->addClassMap($classMap);
50 | }
51 | }
52 |
53 | $loader->register(true);
54 |
55 | return $loader;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/Theme_Updater.php:
--------------------------------------------------------------------------------
1 | 'http://easydigitaldownloads.com',
41 | 'slug' => get_stylesheet(),
42 | 'item_name' => '',
43 | 'license' => '',
44 | 'version' => '',
45 | 'author' => '',
46 | 'beta' => false,
47 | ];
48 |
49 | $args = wp_parse_args( $args, $defaults );
50 |
51 | $this->api_url = $args['api_url'];
52 | $this->api_data = $args;
53 | $this->license = $args['license'];
54 | $this->item_name = $args['item_name'];
55 | $this->version = $args['version'];
56 | $this->slug = sanitize_key( $args['slug'] );
57 | $this->author = $args['author'];
58 | $this->beta = $args['beta'];
59 | $this->cache_key = $args['cache_key'];
60 | $this->response_key = $this->slug . '-' . $this->beta . '-update-response';
61 | $this->strings = $strings;
62 | }
63 |
64 | /**
65 | * Load hooks.
66 | *
67 | * @return void
68 | */
69 | public function load_hooks() {
70 | add_filter( 'site_transient_update_themes', [ $this, 'update_transient' ] );
71 | }
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/vendor/composer/installed.json:
--------------------------------------------------------------------------------
1 | {
2 | "packages": [
3 | {
4 | "name": "afragen/translations-updater",
5 | "version": "dev-master",
6 | "version_normalized": "dev-master",
7 | "source": {
8 | "type": "git",
9 | "url": "https://github.com/afragen/translations-updater.git",
10 | "reference": "93cd3ad14f59e435f0cfa915c6971126095a29f1"
11 | },
12 | "dist": {
13 | "type": "zip",
14 | "url": "https://api.github.com/repos/afragen/translations-updater/zipball/93cd3ad14f59e435f0cfa915c6971126095a29f1",
15 | "reference": "93cd3ad14f59e435f0cfa915c6971126095a29f1",
16 | "shasum": ""
17 | },
18 | "require": {
19 | "php": ">=5.4"
20 | },
21 | "time": "2020-06-24T02:11:27+00:00",
22 | "type": "library",
23 | "installation-source": "dist",
24 | "autoload": {
25 | "psr-4": {
26 | "Fragen\\Translations_Updater\\": "src/Translations_Updater/"
27 | }
28 | },
29 | "notification-url": "https://packagist.org/downloads/",
30 | "license": [
31 | "MIT"
32 | ],
33 | "authors": [
34 | {
35 | "name": "Andy Fragen",
36 | "email": "andy@thefragens.com",
37 | "homepage": "https://thefragens.com",
38 | "role": "Developer"
39 | }
40 | ],
41 | "description": "This framework provides automatic decoupled languate pack updates from a public repository for your WordPress plugin or theme.",
42 | "keywords": [
43 | "language pack",
44 | "plugin",
45 | "theme",
46 | "translations",
47 | "updater",
48 | "wordpress"
49 | ],
50 | "support": {
51 | "issues": "https://github.com/afragen/translations-updater/issues",
52 | "source": "https://github.com/afragen/translations-updater"
53 | },
54 | "install-path": "../afragen/translations-updater"
55 | }
56 | ],
57 | "dev": true,
58 | "dev-package-names": []
59 | }
60 |
--------------------------------------------------------------------------------
/src/License_Form.php:
--------------------------------------------------------------------------------
1 | ' : ' ';
38 | $dashicon = 'theme' === $addon['type'] ? ' ' : $dashicon;
39 |
40 | echo ''; ?>
41 | |
42 |
43 | |
44 |
45 |
46 |
47 |
48 |
49 |
50 | |
51 | ';
54 | wp_nonce_field( $slug . '_nonce', $slug . '_nonce' );
55 | if ( 'valid' === $status ) {
56 | ?>
57 |
58 |
61 |
62 | ';
65 | }
66 | echo '
';
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/vendor/afragen/translations-updater/src/Translations_Updater/Init.php:
--------------------------------------------------------------------------------
1 | caller = $caller;
42 | }
43 |
44 | /**
45 | * Test for proper user capabilities.
46 | *
47 | * @return bool
48 | */
49 | private function can_update() {
50 | // WP-CLI access has full capabilities.
51 | if ( static::is_wp_cli() ) {
52 | return true;
53 | }
54 |
55 | $can_user_update = current_user_can( 'update_plugins' ) && current_user_can( 'update_themes' );
56 |
57 | return $can_user_update;
58 | }
59 |
60 | /**
61 | * Start the processing.
62 | *
63 | * @param mixed $config [ 'git' => '{github|bitbucket|gitlab|gitea}',
64 | * 'type' => '{plugin|theme}',
65 | * 'slug' => 'my-repo-slug',
66 | * 'version => '1.0',
67 | * 'languages' => 'https://github.com//my-translations',
68 | * ].
69 | * @return void|bool
70 | */
71 | public function run( $config ) {
72 | if ( ! isset( $config['git'], $config['type'], $config['slug'], $config['version'], $config['languages'] ) ) {
73 | return false;
74 | }
75 | if ( $this->can_update() ) {
76 | $config = $this->sanitize( $config );
77 | $this->get_remote_repo_data( $config );
78 | }
79 | }
80 |
81 | /**
82 | * Load relevant action hooks for EDD Software Licensing.
83 | */
84 | public function edd_run() {
85 | add_action( 'post_edd_sl_plugin_updater_setup', [ $this, 'parse_edd_config' ], 15, 1 );
86 | add_action( 'post_edd_sl_theme_updater_setup', [ $this, 'parse_edd_config' ], 15, 1 );
87 | }
88 |
89 | /**
90 | * Parse passed config from EDD SL.
91 | *
92 | * @param array $config EDD SL config array.
93 | *
94 | * @return void
95 | */
96 | public function parse_edd_config( $config ) {
97 | $edd_sl_updater = 'EDD\Software_Licensing\Updater';
98 | if ( $edd_sl_updater !== $this->caller ) {
99 | if ( 'post_edd_sl_plugin_updater_setup' === current_filter() ) {
100 | $slug = array_keys( $config )[0];
101 | $config = array_values( $config )[0];
102 | $config['type'] = 'plugin';
103 | $config['slug'] = $slug;
104 | }
105 | if ( 'post_edd_sl_theme_updater_setup' === current_filter() ) {
106 | $config['type'] = 'theme';
107 | $config['slug'] = $config['theme_slug'];
108 | }
109 | }
110 | $this->run( $config );
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/vendor/afragen/translations-updater/src/Translations_Updater/Language_Pack.php:
--------------------------------------------------------------------------------
1 | languages ) {
43 | return;
44 | }
45 |
46 | $this->repo = $config;
47 | $this->repo_api = $api;
48 | }
49 |
50 | /**
51 | * Do the Language Pack integration.
52 | */
53 | public function run() {
54 | $headers = $this->parse_header_uri( $this->repo->languages );
55 | $repo = $this->repo_api->get_language_pack( $headers );
56 | $this->config[ $repo->slug ] = $repo;
57 |
58 | add_filter( 'site_transient_update_plugins', [ $this, 'update_site_transient' ] );
59 | add_filter( 'site_transient_update_themes', [ $this, 'update_site_transient' ] );
60 | }
61 |
62 | /**
63 | * Add language translations to update_plugins or update_themes transients.
64 | *
65 | * @param \stdClass $transient Update transient.
66 | *
67 | * @return mixed
68 | */
69 | public function update_site_transient( $transient ) {
70 | $locales = get_available_languages();
71 | $locales = ! empty( $locales ) ? $locales : [ get_locale() ];
72 | $repos = [];
73 |
74 | if ( ! isset( $transient->translations ) ) {
75 | return $transient;
76 | }
77 |
78 | if ( 'site_transient_update_plugins' === current_filter() ) {
79 | $translations = wp_get_installed_translations( 'plugins' );
80 | }
81 | if ( 'site_transient_update_themes' === current_filter() ) {
82 | $translations = wp_get_installed_translations( 'themes' );
83 | }
84 |
85 | $repos = array_filter(
86 | $this->config,
87 | function ( $e ) {
88 | return isset( $e->language_packs );
89 | }
90 | );
91 |
92 | foreach ( $repos as $repo ) {
93 | foreach ( $locales as $locale ) {
94 | $lang_pack_mod = isset( $repo->language_packs->$locale )
95 | ? strtotime( $repo->language_packs->$locale->updated )
96 | : 0;
97 | $translation_mod = isset( $translations[ $repo->slug ][ $locale ] )
98 | ? strtotime( $translations[ $repo->slug ][ $locale ]['PO-Revision-Date'] )
99 | : 0;
100 | if ( $lang_pack_mod > $translation_mod ) {
101 | $transient->translations[] = (array) $repo->language_packs->$locale;
102 | }
103 | }
104 | }
105 |
106 | $transient->translations = array_unique( $transient->translations, SORT_REGULAR );
107 |
108 | return $transient;
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/Plugin_Updater.php:
--------------------------------------------------------------------------------
1 | 'http://easydigitaldownloads.com',
49 | 'slug' => '',
50 | 'item_name' => '',
51 | 'license' => '',
52 | 'version' => '',
53 | 'author' => '',
54 | 'beta' => false,
55 | ];
56 |
57 | $args = wp_parse_args( $args, $defaults );
58 |
59 | $this->license = $args['license'];
60 | $this->api_url = $args['api_url'];
61 | $this->api_data = $args;
62 | $this->file = $args['file'];
63 | $this->slug = $args['slug'];
64 | $this->item_name = $args['item_name'];
65 | $this->author = $args['author'];
66 | $this->version = $args['version'];
67 | $this->wp_override = $args['wp_override'];
68 | $this->beta = $args['beta'];
69 | $this->cache_key = $args['cache_key'];
70 | $this->strings = $strings;
71 | $edd_plugin_data[ $this->slug ] = $this->api_data;
72 | }
73 |
74 | /**
75 | * Set up WordPress filters to hook into WP's update process.
76 | *
77 | * @return void
78 | */
79 | public function load_hooks() {
80 | add_filter( 'site_transient_update_plugins', [ $this, 'update_transient' ], 15, 1 );
81 | add_filter( 'plugins_api', [ $this, 'plugins_api' ], 99, 3 );
82 | }
83 |
84 | /**
85 | * Updates information on the "View version x.x details" and "View details" page with custom data.
86 | *
87 | * @param mixed $data Default false.
88 | * @param string $action The type of information being requested from the Plugin Installation API.
89 | * @param object $args Plugin API arguments.
90 | * @return object $data
91 | */
92 | public function plugins_api( $data, $action = '', $args = null ) {
93 | if ( 'plugin_information' !== $action ) {
94 | return $data;
95 | }
96 |
97 | if ( ! isset( $args->slug ) || ( $this->slug !== $args->slug ) ) {
98 | return $data;
99 | }
100 |
101 | $data = $this->get_repo_api_data( 'plugin' );
102 |
103 | return $data;
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/vendor/afragen/translations-updater/src/Translations_Updater/Base.php:
--------------------------------------------------------------------------------
1 | languages ) ) {
47 | return false;
48 | }
49 |
50 | $this->config[ $config->slug ] = $config;
51 | $language_pack = new Language_Pack( $config, new Language_Pack_API( $config ) );
52 | $language_pack->run();
53 |
54 | return true;
55 | }
56 |
57 | /**
58 | * Parse URI param returning array of parts.
59 | *
60 | * @param string $repo_header Repository URI.
61 | *
62 | * @return array $header
63 | */
64 | protected function parse_header_uri( $repo_header ) {
65 | $header_parts = parse_url( $repo_header );
66 | $header_path = pathinfo( $header_parts['path'] );
67 | $header['original'] = $repo_header;
68 | $header['scheme'] = isset( $header_parts['scheme'] ) ? $header_parts['scheme'] : null;
69 | $header['host'] = isset( $header_parts['host'] ) ? $header_parts['host'] : null;
70 | $header['type'] = explode( '.', $header['host'] )[0] . '_' . $this->repo->type;
71 | $header['owner'] = trim( $header_path['dirname'], '/' );
72 | $header['repo'] = $header_path['filename'];
73 | $header['owner_repo'] = implode( '/', [ $header['owner'], $header['repo'] ] );
74 | $header['base_uri'] = str_replace( $header_parts['path'], '', $repo_header );
75 | $header['uri'] = isset( $header['scheme'] ) ? trim( $repo_header, '/' ) : null;
76 |
77 | $header = $this->sanitize( $header );
78 |
79 | return $header;
80 | }
81 |
82 | /**
83 | * Sanitize each setting field as needed.
84 | *
85 | * @param array $input Contains all settings fields as array keys.
86 | *
87 | * @return array
88 | */
89 | public function sanitize( $input ) {
90 | $new_input = [];
91 | foreach ( array_keys( (array) $input ) as $id ) {
92 | $new_input[ sanitize_file_name( $id ) ] = sanitize_text_field( $input[ $id ] );
93 | }
94 |
95 | return $new_input;
96 | }
97 |
98 | /**
99 | * Delete options from database.
100 | *
101 | * @return void
102 | */
103 | public function delete_cached_data() {
104 | global $wpdb;
105 |
106 | $table = is_multisite() ? $wpdb->base_prefix . 'sitemeta' : $wpdb->base_prefix . 'options';
107 | $column = is_multisite() ? 'meta_key' : 'option_name';
108 | $delete_string = 'DELETE FROM ' . $table . ' WHERE ' . $column . ' LIKE %s LIMIT 1000';
109 |
110 | $wpdb->query( $wpdb->prepare( $delete_string, [ '%tu-%' ] ) );
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/src/Theme_Updater_Admin.php:
--------------------------------------------------------------------------------
1 | 'http://easydigitaldownloads.com',
48 | 'slug' => get_template(),
49 | 'item_name' => '',
50 | 'item_id' => '',
51 | 'download_id' => '',
52 | 'license' => '',
53 | 'version' => '',
54 | 'author' => '',
55 | 'renew_url' => '',
56 | 'beta' => false,
57 | ]
58 | );
59 |
60 | // Set config arguments.
61 | $this->api_url = $config['api_url'];
62 | $this->item_name = $config['item_name'];
63 | $this->item_id = $config['item_id'];
64 | $this->download_id = $config['download_id'];
65 | $this->slug = sanitize_key( $config['slug'] );
66 | $this->license = ! empty( $config['license'] ) ? $config['license'] : trim( get_site_option( $this->slug . '_license_key' ) );
67 | $this->api_data = $config;
68 | $this->version = $config['version'];
69 | $this->author = $config['author'];
70 | $this->renew_url = $config['renew_url'];
71 | $this->beta = $config['beta'];
72 | $this->cache_key = 'edd_sl_' . md5( json_encode( $this->slug . $this->api_data['license'] . $this->beta ) );
73 |
74 | // Populate version fallback.
75 | if ( empty( $config['version'] ) ) {
76 | $theme = wp_get_theme( $this->slug );
77 | $this->version = $theme->get( 'Version' );
78 | }
79 |
80 | $this->strings = $this->get_strings();
81 | $this->data = $config;
82 |
83 | /**
84 | * Fires after the theme $config is setup.
85 | *
86 | * @since 1.0.0
87 | *
88 | * @param array $config Array of EDD SL theme data.
89 | */
90 | do_action( 'post_edd_sl_theme_updater_setup', $config );
91 | }
92 |
93 | /**
94 | * Load all our hooks.
95 | *
96 | * @return void
97 | */
98 | public function load_hooks() {
99 | add_action( 'init', [ $this, 'updater' ] );
100 | $this->load_settings();
101 | }
102 |
103 | /**
104 | * Load hooks for licence settings.
105 | *
106 | * @return void
107 | */
108 | public function load_settings() {
109 | add_action( 'admin_init', [ $this, 'register_option' ] );
110 | add_action( 'admin_init', [ $this, 'license_action' ] );
111 | add_action( 'admin_notices', [ $this, 'show_error' ] );
112 | add_action( 'admin_init', [ $this, 'update_settings' ] );
113 | add_filter( 'http_request_args', [ $this, 'disable_wporg_request' ], 5, 2 );
114 | add_filter( 'edd_sl_updater_add_admin_page', [ $this, 'license_page' ] );
115 | }
116 |
117 | /**
118 | * Creates the updater class.
119 | */
120 | public function updater() {
121 | // Kludge to override capability check when doing cron.
122 | $doing_cron = defined( 'DOING_CRON' ) && DOING_CRON;
123 | if ( ! current_user_can( 'manage_options' ) && ! $doing_cron ) {
124 | return;
125 | }
126 |
127 | ( new Theme_Updater(
128 | [
129 | 'api_url' => $this->api_url,
130 | 'api_data' => $this->api_data,
131 | 'version' => $this->version,
132 | 'license' => $this->license,
133 | 'item_name' => $this->item_name,
134 | 'item_id' => $this->item_id,
135 | 'author' => $this->author,
136 | 'beta' => $this->beta,
137 | 'cache_key' => $this->cache_key,
138 | ],
139 | $this->strings
140 | ) )->load_hooks();
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/vendor/afragen/translations-updater/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Translations Updater
3 |
4 | * Contributors: [Andy Fragen](https://github.com/afragen)
5 | * Tags: plugins, themes, edd software licensing, language pack, updater
6 | * Requires at least: 4.6
7 | * Requires PHP: 5.4
8 | * Donate link:
9 | * License: MIT
10 | * License URI:
11 |
12 | ## Description
13 |
14 | This framework allows for decoupled language pack updates for your WordPress plugins or themes that are hosted on public repositories in GitHub, Bitbucket, GitLab, or Gitea.
15 |
16 | The URI should point to a repository that contains the translations files. Refer to [GitHub Updater Translations](https://github.com/afragen/github-updater-translations) as an example. It is created using the [Language Pack Maker](https://github.com/afragen/language-pack-maker). The repo **must** be a public repo.
17 |
18 | ## Usage
19 |
20 | Install via Composer: `composer require afragen/translations-updater:dev-master`
21 |
22 | **Prior to release use the following command**
23 | `composer require afragen/translations-updater:dev-` currently `dev-master`
24 |
25 | Add `require_once __DIR__ . '/vendor/autoload.php';` to the main plugin file or theme's functions.php file.
26 |
27 | A configuration array with the following format is needed. All array elements are required.
28 |
29 | ```php
30 | add_action( 'admin_init', function() {
31 | $config = [
32 | 'git' => '(github|bitbucket|gitlab|gitea)',
33 | 'type' => '(plugin|theme)',
34 | 'slug' => 'my-repo-slug',
35 | 'version' => 'my-repo-version', // Current version of plugin|theme.
36 | 'languages' => 'https://my-path-to/language-packs',
37 | ];
38 |
39 | ( new \Fragen\Translations_Updater\Init() )->run( $config );
40 | } );
41 | ```
42 |
43 | If you wish to delete the data stored in the options table associated with this framework you will need to issue the following command.
44 |
45 | ```php
46 | ( new \Fragen\Translations_Updater\Init() )->delete_cached_data();
47 | ```
48 |
49 | ## EDD Software Licensing Usage
50 |
51 | If using this framework with EDD Software Licensing you will need to update to the latest versions of the updaters in the EDD Software Licensing sample code to ensure the appropriate action hooks are present.
52 |
53 | You will need to add two key/value pairs to your setup array similar to the following,
54 | ```php
55 | 'git' => 'github',
56 | 'languages' => 'https://github.com//my-language-pack',
57 | ```
58 |
59 | You will need to include the following command to your bootstrap file to activate the updater.
60 |
61 | ```php
62 | ( new \Fragen\Translations_Updater\Init( __NAMESPACE__ ) )->edd_run();
63 | ```
64 |
65 | ### Plugins
66 |
67 | You must add two additional key/value pairs to the setup array in your `EDD_SL_Plugin_Updater` setup. The array will be similar to the following from the `edd-sample-plugin.php` file.
68 |
69 | ```php
70 | $edd_updater = new EDD_SL_Plugin_Updater( EDD_SAMPLE_STORE_URL, __FILE__, array(
71 | 'version' => '1.0', // current version number
72 | 'license' => $license_key, // license key (used get_option above to retrieve from DB)
73 | 'item_name' => EDD_SAMPLE_ITEM_NAME, // name of this plugin
74 | 'author' => 'Pippin Williamson', // author of this plugin
75 | 'beta' => false,
76 | 'git' => 'bitbucket',
77 | 'languages' => 'https://bitbucket.org/afragen/test-language-pack',
78 | )
79 | ```
80 |
81 | ### Themes
82 |
83 | You must add two additional key/value pairs to the setup array in your `EDD_Theme_Updater_Admin` setup. The array will be similar to the following from the `edd-sample-theme/updater/theme-updater.php` file.
84 |
85 | ```php
86 | $updater = new EDD_Theme_Updater_Admin(
87 |
88 | // Config settings
89 | $config = array(
90 | 'remote_api_url' => 'https://easydigitaldownloads.com', // Site where EDD is hosted
91 | 'item_name' => 'Theme Name', // Name of theme
92 | 'theme_slug' => 'theme-slug', // Theme slug
93 | 'version' => '1.0.0', // The current version of this theme
94 | 'author' => 'Easy Digital Downloads', // The author of this theme
95 | 'download_id' => '', // Optional, used for generating a license renewal link
96 | 'renew_url' => '', // Optional, allows for a custom license renewal link
97 | 'beta' => false, // Optional, set to true to opt into beta versions
98 | 'git' => 'github',
99 | 'languages' => 'https://github.com//my-language-pack',
100 | ),
101 | ...
102 | ```
103 |
--------------------------------------------------------------------------------
/vendor/afragen/translations-updater/src/Translations_Updater/API.php:
--------------------------------------------------------------------------------
1 | repo->type;
48 |
49 | switch ( $this->repo->git ) {
50 | case 'github':
51 | $arr['git'] = 'github';
52 | $arr['base_uri'] = 'https://api.github.com';
53 | $arr['base_download'] = 'https://github.com';
54 | break;
55 | case 'bitbucket':
56 | $arr['git'] = 'bitbucket';
57 | $arr['base_uri'] = 'https://bitbucket.org/api';
58 | $arr['base_download'] = 'https://bitbucket.org';
59 | break;
60 | case 'gitlab':
61 | $arr['git'] = 'gitlab';
62 | $arr['base_uri'] = 'https://gitlab.com/api/v4';
63 | $arr['base_download'] = 'https://gitlab.com';
64 | break;
65 | case 'gitea':
66 | $arr['git'] = 'gitea';
67 | // TODO: make sure this works.
68 | $arr['base_uri'] = $this->repo->languages . '/api/v1';
69 | $arr['base_download'] = $this->repo->languages;
70 | break;
71 | }
72 |
73 | return $arr;
74 | }
75 |
76 | /**
77 | * Call the API and return a json decoded body.
78 | *
79 | * @param string $url Repository URL.
80 | *
81 | * @return boolean|\stdClass
82 | */
83 | protected function api( $url ) {
84 | $response = wp_remote_get( $this->get_api_url( $url ) );
85 |
86 | if ( is_wp_error( $response ) ) {
87 | return false;
88 | }
89 |
90 | return json_decode( wp_remote_retrieve_body( $response ) );
91 | }
92 |
93 | /**
94 | * Return API url.
95 | *
96 | * @access protected
97 | *
98 | * @param string $endpoint API endpoint.
99 | *
100 | * @return string $endpoint
101 | */
102 | protected function get_api_url( $endpoint ) {
103 | $type = $this->return_repo_type();
104 |
105 | switch ( $type['git'] ) {
106 | case 'github':
107 | case 'bitbucket':
108 | case 'gitea':
109 | break;
110 | case 'gitlab':
111 | $endpoint = add_query_arg( 'ref', 'master', $endpoint );
112 | break;
113 | default:
114 | }
115 |
116 | return $type['base_uri'] . $endpoint;
117 | }
118 |
119 | /**
120 | * Validate wp_remote_get response.
121 | *
122 | * @param \stdClass $response API response.
123 | *
124 | * @return bool true if invalid
125 | */
126 | protected function validate_response( $response ) {
127 | return empty( $response ) || isset( $response->message );
128 | }
129 |
130 | /**
131 | * Returns repo cached data.
132 | *
133 | * @param string|bool $repo Repo name or false.
134 | *
135 | * @return array|bool false for expired cache
136 | */
137 | protected function get_repo_cache( $repo = false ) {
138 | if ( ! $repo ) {
139 | $repo = isset( $this->type->slug ) ? $this->type->slug : 'tu';
140 | }
141 | $cache_key = 'tu-' . md5( $repo );
142 | $cache = get_site_option( $cache_key );
143 |
144 | if ( empty( $cache['timeout'] ) || time() > $cache['timeout'] ) {
145 | return false;
146 | }
147 |
148 | return $cache;
149 | }
150 |
151 | /**
152 | * Sets repo data for cache in site option.
153 | *
154 | * @param string $id Data Identifier.
155 | * @param mixed $response Data to be stored.
156 | * @param string|bool $repo Repo name or false.
157 | *
158 | * @return bool
159 | */
160 | protected function set_repo_cache( $id, $response, $repo = false ) {
161 | if ( ! $repo ) {
162 | $repo = isset( $this->type->slug ) ? $this->type->slug : 'tu';
163 | }
164 | $cache_key = 'tu-' . md5( $repo );
165 | $timeout = '+' . self::$hours . ' hours';
166 |
167 | $this->response['timeout'] = strtotime( $timeout );
168 | $this->response[ $id ] = $response;
169 |
170 | update_site_option( $cache_key, $this->response );
171 |
172 | return true;
173 | }
174 | }
175 |
--------------------------------------------------------------------------------
/languages/edd-sl-updater.pot:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2021 Andy Fragen
2 | # This file is distributed under the same license as the EDD Software Licensing Updater plugin.
3 | msgid ""
4 | msgstr ""
5 | "Project-Id-Version: EDD Software Licensing Updater 0.11.16\n"
6 | "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/edd-sl-updater\n"
7 | "Last-Translator: FULL NAME \n"
8 | "Language-Team: LANGUAGE \n"
9 | "MIME-Version: 1.0\n"
10 | "Content-Type: text/plain; charset=UTF-8\n"
11 | "Content-Transfer-Encoding: 8bit\n"
12 | "POT-Creation-Date: 2021-04-08T23:52:49+00:00\n"
13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
14 | "X-Generator: WP-CLI 2.4.0\n"
15 | "X-Domain: edd-sl-updater\n"
16 |
17 | #. Plugin Name of the plugin
18 | msgid "EDD Software Licensing Updater"
19 | msgstr ""
20 |
21 | #. Plugin URI of the plugin
22 | msgid "https://github.com/afragen/edd-sl-updater"
23 | msgstr ""
24 |
25 | #. Description of the plugin
26 | msgid "A universal updater for EDD Software Licensing products."
27 | msgstr ""
28 |
29 | #. Author of the plugin
30 | msgid "Andy Fragen"
31 | msgstr ""
32 |
33 | #: src/API_Common.php:45
34 | msgid "WP_Error"
35 | msgstr ""
36 |
37 | #: src/API_Common.php:53
38 | msgid "HTTP Error Code"
39 | msgstr ""
40 |
41 | #. translators: %s: item name
42 | #: src/License_Actions.php:111
43 | #: src/License_Actions.php:163
44 | msgid "activate_license-%s"
45 | msgstr ""
46 |
47 | #: src/License_Actions.php:178
48 | msgid "Theme License"
49 | msgstr ""
50 |
51 | #: src/License_Actions.php:179
52 | msgid "Plugin License"
53 | msgstr ""
54 |
55 | #: src/License_Actions.php:180
56 | msgid "Enter your license key."
57 | msgstr ""
58 |
59 | #: src/License_Actions.php:181
60 | #: src/Settings.php:78
61 | msgid "License Key"
62 | msgstr ""
63 |
64 | #: src/License_Actions.php:182
65 | #: src/Settings.php:80
66 | msgid "License Action"
67 | msgstr ""
68 |
69 | #: src/License_Actions.php:183
70 | msgid "Deactivate License"
71 | msgstr ""
72 |
73 | #: src/License_Actions.php:184
74 | msgid "Activate License"
75 | msgstr ""
76 |
77 | #: src/License_Actions.php:185
78 | #: src/License_Actions.php:205
79 | msgid "License status is unknown."
80 | msgstr ""
81 |
82 | #: src/License_Actions.php:186
83 | msgid "Invalid License."
84 | msgstr ""
85 |
86 | #. translators: %s: item name
87 | #: src/License_Actions.php:188
88 | msgid "This appears to be an invalid license key for %s."
89 | msgstr ""
90 |
91 | #: src/License_Actions.php:189
92 | msgid "Renew?"
93 | msgstr ""
94 |
95 | #: src/License_Actions.php:190
96 | msgid "unlimited"
97 | msgstr ""
98 |
99 | #: src/License_Actions.php:191
100 | msgid "License key is active."
101 | msgstr ""
102 |
103 | #. translators: %s: expiration date
104 | #: src/License_Actions.php:193
105 | msgid "Expires %s."
106 | msgstr ""
107 |
108 | #: src/License_Actions.php:194
109 | msgid "Lifetime License."
110 | msgstr ""
111 |
112 | #. translators: %1: number of sites activated, %2: total number of sites activated
113 | #: src/License_Actions.php:196
114 | msgid "You have %1$s / %2$s sites activated."
115 | msgstr ""
116 |
117 | #. translators: %s: expiration date
118 | #: src/License_Actions.php:198
119 | msgid "License key expired on %s."
120 | msgstr ""
121 |
122 | #: src/License_Actions.php:199
123 | msgid "License key has expired."
124 | msgstr ""
125 |
126 | #: src/License_Actions.php:200
127 | msgid "License keys do not match."
128 | msgstr ""
129 |
130 | #: src/License_Actions.php:201
131 | msgid "License is inactive."
132 | msgstr ""
133 |
134 | #: src/License_Actions.php:202
135 | msgid "License key is disabled."
136 | msgstr ""
137 |
138 | #: src/License_Actions.php:203
139 | msgid "Your license is not active for this URL."
140 | msgstr ""
141 |
142 | #: src/License_Actions.php:204
143 | msgid "Site is inactive."
144 | msgstr ""
145 |
146 | #: src/License_Actions.php:206
147 | msgid "Your license key has reached its activation limit."
148 | msgstr ""
149 |
150 | #: src/License_Actions.php:207
151 | msgid "Updating this theme will lose any customizations you have made. 'Cancel' to stop, 'OK' to update."
152 | msgstr ""
153 |
154 | #. translators: %1: Name, %2: new version, %3: URL, %4: link title, %5: URL, %6: opening tag
155 | #: src/License_Actions.php:209
156 | msgid "%1$s %2$s is available. Check out what's new or update now."
157 | msgstr ""
158 |
159 | #: src/License_Actions.php:210
160 | msgid "An error occurred, please try again."
161 | msgstr ""
162 |
163 | #: src/Settings.php:55
164 | #: src/Settings.php:70
165 | msgid "EDD SL Licenses"
166 | msgstr ""
167 |
168 | #: src/Settings.php:56
169 | msgctxt "Menu item"
170 | msgid "EDD SL Licenses"
171 | msgstr ""
172 |
--------------------------------------------------------------------------------
/vendor/afragen/translations-updater/src/Translations_Updater/Language_Pack_API.php:
--------------------------------------------------------------------------------
1 | repo = $config;
35 | $this->response = $this->get_repo_cache( $config->slug );
36 | }
37 |
38 | /**
39 | * Get/process Language Packs.
40 | *
41 | * @param array $headers Array of headers of Language Pack.
42 | *
43 | * @return bool When invalid response.
44 | */
45 | public function get_language_pack( $headers ) {
46 | $response = ! empty( $this->response['languages'] ) ? $this->response['languages'] : false;
47 |
48 | if ( ! $response ) {
49 | $response = $this->get_language_pack_json( $this->repo->git, $headers );
50 |
51 | if ( $response ) {
52 | foreach ( $response as $locale ) {
53 | $package = $this->process_language_pack_package( $this->repo->git, $locale, $headers );
54 |
55 | $response->{$locale->language}->package = $package;
56 | $response->{$locale->language}->type = $this->repo->type;
57 | $response->{$locale->language}->version = $this->repo->version;
58 | }
59 | $this->set_repo_cache( 'languages', $response, $this->repo->slug );
60 | } else {
61 | return false;
62 | }
63 | }
64 | $this->repo->language_packs = $response;
65 |
66 | return $this->repo;
67 | }
68 |
69 | /**
70 | * Get language-pack.json from appropriate host.
71 | *
72 | * @param string $git ( github|bitbucket|gitlab|gitea ).
73 | * @param array $headers Repository headers.
74 | *
75 | * @return array|bool|mixed|object $response API response object.
76 | */
77 | private function get_language_pack_json( $git, $headers ) {
78 | switch ( $git ) {
79 | case 'github':
80 | $response = $this->api( '/repos/' . $headers['owner'] . '/' . $headers['repo'] . '/contents/language-pack.json' );
81 | $response = isset( $response->content )
82 | ? json_decode( base64_decode( $response->content ) )
83 | : null;
84 | break;
85 | case 'bitbucket':
86 | $response = $this->api( '/2.0/repositories/' . $headers['owner'] . '/' . $headers['repo'] . '/src/master/language-pack.json' );
87 | break;
88 | case 'gitlab':
89 | $id = rawurlencode( $headers['owner'] . '/' . $headers['repo'] );
90 | $response = $this->api( '/projects/' . $id . '/repository/files/language-pack.json' );
91 | $response = isset( $response->content )
92 | ? json_decode( base64_decode( $response->content ) )
93 | : null;
94 | break;
95 | case 'gitea':
96 | $response = $this->api( '/repos/' . $headers['owner'] . '/' . $headers['repo'] . '/raw/master/language-pack.json' );
97 | $response = isset( $response->content )
98 | ? json_decode( base64_decode( $response->content ) )
99 | : null;
100 | break;
101 | }
102 |
103 | if ( $this->validate_response( $response ) ) {
104 | return false;
105 | }
106 |
107 | return $response;
108 | }
109 |
110 | /**
111 | * Process $package for update transient.
112 | *
113 | * @param string $git ( github|bitbucket|gitlab|gitea ).
114 | * @param string $locale Site locale.
115 | * @param array $headers Repository headers.
116 | *
117 | * @return array|null|string
118 | */
119 | private function process_language_pack_package( $git, $locale, $headers ) {
120 | $package = null;
121 | switch ( $git ) {
122 | case 'github':
123 | $package = [ $headers['uri'], 'blob/master' ];
124 | $package = implode( '/', $package ) . $locale->package;
125 | $package = add_query_arg( [ 'raw' => 'true' ], $package );
126 | break;
127 | case 'bitbucket':
128 | $package = [ $headers['uri'], 'raw/master' ];
129 | $package = implode( '/', $package ) . $locale->package;
130 | break;
131 | case 'gitlab':
132 | $package = [ $headers['uri'], 'raw/master' ];
133 | $package = implode( '/', $package ) . $locale->package;
134 | break;
135 | case 'gitea':
136 | // TODO: make sure this works.
137 | $package = [ $headers['uri'], 'raw/master' ];
138 | $package = implode( '/', $package ) . $local->package;
139 | break;
140 | }
141 |
142 | return $package;
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/src/Plugin_Updater_Admin.php:
--------------------------------------------------------------------------------
1 | '',
50 | 'api_url' => 'http://easydigitaldownloads.com',
51 | 'item_name' => '',
52 | 'item_id' => '',
53 | 'download_id' => '',
54 | 'version' => '',
55 | 'license' => '',
56 | 'author' => '',
57 | 'renew_url' => '',
58 | 'beta' => false,
59 | ]
60 | );
61 |
62 | // Set config arguments.
63 | $this->api_url = $config['api_url'];
64 | $this->item_name = $config['item_name'];
65 | $this->item_id = $config['item_id'];
66 | $this->download_id = $config['item_id'];
67 | $this->file = plugin_basename( $config['file'] );
68 | $this->slug = dirname( $this->file );
69 | $this->version = $config['version'];
70 | $this->author = $config['author'];
71 | $this->renew_url = $config['renew_url'];
72 | $this->beta = $config['beta'];
73 | $this->license = ! empty( $config['license'] ) ? $config['license'] : trim( get_site_option( $this->slug . '_license_key' ) );
74 | $this->api_data = $config;
75 | $this->version = $config['version'];
76 | $this->wp_override = isset( $config['wp_override'] ) ? (bool) $config['wp_override'] : false;
77 | $this->beta = ! empty( $this->api_data['beta'] ) ? true : false;
78 | $this->cache_key = 'edd_sl_' . md5( json_encode( $this->slug . $this->api_data['license'] . $this->beta ) );
79 |
80 | $edd_plugin_data[ $this->slug ] = $this->api_data;
81 |
82 | // Populate version fallback.
83 | if ( empty( $config['version'] ) ) {
84 | $plugin = get_file_data( $config['file'], 'plugin' );
85 | $this->version = $plugin['Version'];
86 | }
87 |
88 | $config['slug'] = $this->slug;
89 | $config['file'] = $this->file;
90 | $this->strings = $this->get_strings();
91 | $this->data = $config;
92 |
93 | /**
94 | * Fires after the $config is setup.
95 | *
96 | * @since 1.0.0
97 | *
98 | * @param array $config Array of EDD SL plugin data.
99 | */
100 | do_action( 'post_edd_sl_plugin_updater_setup', $config );
101 | }
102 |
103 | /**
104 | * Load all our hooks.
105 | *
106 | * @return void
107 | */
108 | public function load_hooks() {
109 | add_action( 'init', [ $this, 'updater' ] );
110 | $this->load_settings();
111 | }
112 |
113 | /**
114 | * Load hooks for licence settings.
115 | *
116 | * @return void
117 | */
118 | public function load_settings() {
119 | add_action( 'admin_init', [ $this, 'register_option' ] );
120 | add_action( 'admin_init', [ $this, 'license_action' ] );
121 | add_action( 'admin_notices', [ $this, 'show_error' ] );
122 | add_action( 'admin_init', [ $this, 'update_settings' ] );
123 | add_filter( 'edd_sl_updater_add_admin_page', [ $this, 'license_page' ] );
124 | }
125 |
126 | /**
127 | * Creates the updater class.
128 | *
129 | * @return void
130 | */
131 | public function updater() {
132 | // Kludge to override capability check when doing cron.
133 | $doing_cron = defined( 'DOING_CRON' ) && DOING_CRON;
134 | if ( ! current_user_can( 'manage_options' ) && ! $doing_cron ) {
135 | return;
136 | }
137 |
138 | ( new Plugin_Updater(
139 | [
140 | 'api_url' => $this->api_url,
141 | 'api_data' => $this->api_data,
142 | 'name' => $this->item_name,
143 | 'file' => $this->file,
144 | 'item_name' => $this->item_name,
145 | 'item_id' => $this->item_id,
146 | 'slug' => $this->slug,
147 | 'version' => $this->version,
148 | 'license' => $this->license,
149 | 'author' => $this->author,
150 | 'wp_override' => $this->wp_override,
151 | 'beta' => $this->beta,
152 | 'cache_key' => $this->cache_key,
153 | ],
154 | $this->strings
155 | ) )->load_hooks();
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/src/Settings.php:
--------------------------------------------------------------------------------
1 | slug . '-license',
36 | $this->slug . '_license_key',
37 | []
38 | );
39 | }
40 |
41 | /**
42 | * Add options page.
43 | */
44 | public function add_plugin_menu() {
45 | global $_registered_pages;
46 | if ( isset( $_registered_pages['settings_page_edd-sl-updater'] ) ) {
47 | return;
48 | }
49 |
50 | $parent = 'options-general.php';
51 | $capability = 'manage_options';
52 |
53 | add_submenu_page(
54 | $parent,
55 | esc_html__( 'EDD SL Licenses', 'edd-sl-updater' ),
56 | esc_html_x( 'EDD SL Licenses', 'Menu item', 'edd-sl-updater' ),
57 | $capability,
58 | 'edd-sl-updater',
59 | [ $this, 'create_admin_page' ]
60 | );
61 | }
62 |
63 | /**
64 | * Options page callback.
65 | */
66 | public function create_admin_page() {
67 | ?>
68 | ';
92 | submit_button();
93 | echo '';
94 | }
95 |
96 | /**
97 | * Update settings.
98 | *
99 | * @return void|bool
100 | */
101 | public function update_settings() {
102 | $_post = $_POST; // phpcs:ignore WordPress.Security.NonceVerification.Missing
103 | if ( ! isset( $_post['_wp_http_referer'] ) ) {
104 | return false;
105 | }
106 | $query = parse_url( esc_url_raw( wp_unslash( $_post['_wp_http_referer'] ) ), PHP_URL_QUERY );
107 | parse_str( $query, $arr );
108 |
109 | if ( isset( $_post['option_page'] ) &&
110 | 'edd-sl-updater' === $arr['page']
111 | ) {
112 | foreach ( array_keys( $_post ) as $key ) {
113 | if ( false !== strpos( $key, '_deactivate' ) ) {
114 | return;
115 | }
116 | if ( false !== strpos( $key, '_activate' ) ) {
117 | return;
118 | }
119 | }
120 |
121 | foreach ( $_post as $option => $value ) {
122 | if ( false !== strpos( $option, '_license_key' ) ) {
123 | $slug = str_replace( '_license_key', '', $option );
124 | $value = $this->sanitize_license( $slug, $value );
125 | update_site_option( sanitize_key( $option ), sanitize_text_field( $value ) );
126 | delete_transient( $slug . '_license_message' );
127 | }
128 | }
129 | $this->redirect();
130 | }
131 | }
132 |
133 | /**
134 | * Sanitize license.
135 | *
136 | * @param string $slug Slug.
137 | * @param string $new License.
138 | *
139 | * @return string $new
140 | */
141 | public function sanitize_license( $slug, $new ) {
142 | $old = get_site_option( $slug . '_license_key' );
143 | if ( $old && $old !== $new ) {
144 | delete_site_option( $this->slug . '_license_key_status' );
145 | delete_transient( $this->slug . '_license_message' );
146 | }
147 |
148 | return $new;
149 | }
150 |
151 | /**
152 | * Outputs the markup used on the plugin license page.
153 | */
154 | public function license_page() {
155 | $license = $this->license;
156 | $status = get_site_option( $this->slug . '_license_key_status', false );
157 |
158 | // Checks license status to display under license key.
159 | if ( ! $license ) {
160 | $message = $this->strings['enter-key'];
161 | } else {
162 | // delete_transient( $this->slug . '_license_message' );
163 | if ( ! get_transient( $this->slug . '_license_message', false ) ) {
164 | set_transient( $this->slug . '_license_message', $this->check_license( $this->slug ), DAY_IN_SECONDS );
165 | }
166 | $message = get_transient( $this->slug . '_license_message' );
167 | }
168 | settings_fields( $this->data['slug'] . '_license' );
169 | $form_table_row = ( new License_Form() )->row( $this->data, $license, $status, $message, $this->strings );
170 | /**
171 | * Filter to echo a customized license form table.
172 | *
173 | * @since 1.0.0
174 | *
175 | * @param string $form_table Table HTML for a license page setting.
176 | * @param string $plugin_data EDD SL Add-on data.
177 | * @param string $license EDD SL license.
178 | * @param string $status License status.
179 | * @param string $message License message.
180 | * @param array $strings Messaging strings.
181 | */
182 | echo esc_html( apply_filters( 'edd_sl_license_form_table', $form_table_row, $this->data, $license, $status, $message, $this->strings ) );
183 | }
184 | }
185 |
--------------------------------------------------------------------------------
/readme.txt:
--------------------------------------------------------------------------------
1 | # EDD Sofware Licensing Updater
2 |
3 | Author URI: https://easydigitaldownloads.com
4 | Plugin URI: https://easydigitaldownloads.com
5 | Contributors: easydigitaldownloads, afragen
6 | Donate link: https://easydigitaldownloads.com/donate/
7 | Tags: easydigitaldownloads, updater
8 | Requires at least: 5.2
9 | Requires PHP: 5.6
10 | Tested up to: 5.5
11 | Stable Tag: x.x
12 | License: MIT
13 |
14 | ## Description
15 |
16 | A universal updater for EDD Software Licensing products.
17 |
18 | This plugin would be installed by all users of EDD Software Licensing Addons so they would be able to see updates from the store. This allows EDD Addon developers to simplify the code required to access the update system from EDD Software Licensing.
19 |
20 | PRs welcome at [EDD SL Updater](https://github.com/afragen/edd-sl-updater).
21 |
22 | ## Installation for EDD SL Add-ons
23 |
24 | The following code examples show how to instantiate and install the EDD SL Updater plugin for EDD SL Add-on plugins and themes.
25 |
26 | In the samples, the `wp-dependency.json` file **must** be included with all EDD SL Add-ons. To add the appropriate elements to your `composer.json` run the following command from your plugin/theme folder. If you have done this previously please ensure you are using the _latest_ version of `wp-dependency-installer` by adjusting your `composer.json`, if needed.
27 |
28 | composer require afragen/wp-dependency-installer
29 |
30 | The code installed via composer will automatically install and activate the EDD SL Updater plugin as a required dependency for any EDD SL Add-on.
31 |
32 | Additionally, you may need then need to run `composer update` prior to plugin distribution.
33 |
34 | The EDD SL Updater now uses a universal instantiation function that will correctly instantiate your plugin or theme based upon the configuration array passed to the function.
35 |
36 | ### Plugin Updater Example
37 |
38 | The following is an example of how to instantiate the settings/updater from a plugin. It is run from the main plugin file. This will provide a settings page for the plugin too.
39 |
40 | // Automatically install EDD SL Updater.
41 | require_once __DIR__ . '/vendor/autoload.php';
42 | \WP_Dependency_Installer::instance( __DIR__ )->run();
43 |
44 | // Loads the updater classes
45 | function prefix_plugin_updater() {
46 | $config = [
47 | 'type' => 'plugin', // Declare the type.
48 | 'file' => __FILE__,
49 | 'api_url' => 'http://eddstore.test', // Site where EDD SL store is located.
50 | 'item_name' => 'EDD Test Plugin', // Name of plugin.
51 | 'item_id' => 11, // ID of the product.
52 | 'version' => '1.0', // Current version number.
53 | 'author' => 'Andy Fragen', // Author of this plugin.
54 | 'beta' => false,
55 | ];
56 | if ( class_exists( 'EDD\\Software_Licensing\\Updater\\Bootstrap' ) ) {
57 | ( new EDD\Software_Licensing\Updater\Init() )->run( $config );
58 | }
59 | }
60 | add_action( 'plugins_loaded', 'prefix_plugin_updater' );
61 |
62 |
63 | ### Theme Updater Example
64 |
65 | The following is an example of how to instantiate the settings/updater from a theme. It is run from the theme's `functions.php` file. This will provide a settings page for your theme too.
66 |
67 | // Automatically install EDD SL Updater.
68 | require_once __DIR__ . '/vendor/autoload.php';
69 | \WP_Dependency_Installer::instance( __DIR__ )->run();
70 |
71 | // Loads the updater classes
72 | function prefix_theme_updater() {
73 | $config = [
74 | 'type' => 'theme', // Declare the type.
75 | 'api_url' => 'http://eddstore.test', // Site where EDD SL store is located.
76 | 'item_name' => 'EDD Test Theme', // Name of theme.
77 | 'item_id' => 27, // ID of the product.
78 | 'slug' => 'edd-test-theme', // Theme slug.
79 | 'version' => '1.0', // Current version of this theme.
80 | 'author' => 'Andy Fragen', // Author of this theme
81 | 'download_id' => '', // Optional, used for generating a license renewal link.
82 | 'renew_url' => '', // Optional, allows for a custom license renewal link.
83 | 'beta' => false, // Optional, set to true to opt into beta versions.
84 | ];
85 | if ( class_exists( 'EDD\\Software_Licensing\\Updater\\Bootstrap' ) ) {
86 | ( new EDD\Software_Licensing\Updater\Init() )->run( $config );
87 | }
88 | }
89 | add_action( 'after_setup_theme', 'prefix_theme_updater' );
90 |
91 | ### Updater Only
92 |
93 | If your plugin or theme creates and manages it's own settings you can simply activate only the updater by changing the init code line to the following.
94 |
95 | ( new EDD\Software_Licensing\Updater\Init() )->updater( $config );
96 |
97 | You must save/get your plugin/theme license in an option with the format `get_option( $slug . '_license_key' );` **or** you can set a key/value pair in the config array to return the license by setting the optional `license` key in the config array. Something like the following.
98 |
99 | 'license' => get_option( 'my-license-key '),
100 |
101 | ### Decoupled Translation Packs
102 |
103 | This added framework allows for decoupled language pack updates. For more complete instructions please refer to [translations-updater framework](https://github.com/afragen/translations-updater).
104 |
105 | The URI should point to a repository that contains the translations files. Refer to [GitHub Updater Translations](https://github.com/afragen/github-updater-translations) as an example. It is created using the [Language Pack Maker](https://github.com/afragen/language-pack-maker). The repo **must** be a public repo.
106 |
107 | You will need to add two key/value pairs to your EDD SL Add-on config array similar to the following,
108 |
109 | 'git' => 'github',
110 | 'languages' => 'https://github.com//my-language-pack',
111 |
112 | ## Changelog
113 | [Changelog](./CHANGES.md)
114 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # EDD Sofware Licensing Updater
2 |
3 | * Author URI: https://easydigitaldownloads.com
4 | * Plugin URI: https://easydigitaldownloads.com
5 | * Contributors: easydigitaldownloads, afragen
6 | * Donate link: https://easydigitaldownloads.com/donate/
7 | * Tags: easydigitaldownloads, updater
8 | * Requires at least: 5.2
9 | * Requires PHP: 5.6
10 | * Stable Tag: master
11 | * License: MIT
12 |
13 | ## Description
14 |
15 | A universal updater for EDD Software Licensing products.
16 |
17 | This plugin would be installed by all users of EDD Software Licensing Addons so they would be able to see updates from the store. This allows EDD Addon developers to simplify the code required to access the update system from EDD Software Licensing.
18 |
19 | PRs welcome at [EDD SL Updater](https://github.com/afragen/edd-sl-updater).
20 |
21 | ## Installation for EDD SL Add-ons
22 |
23 | The following code examples show how to instantiate and install the EDD SL Updater plugin for EDD SL Add-on plugins and themes.
24 |
25 | In the samples, the `wp-dependency.json` file **must** be included with all EDD SL Add-ons. To add the appropriate elements to your `composer.json` run the following command from your plugin/theme folder. If you have done this previously please ensure you are using the _latest_ version of `wp-dependency-installer` by adjusting your `composer.json`, if needed.
26 |
27 | `composer require afragen/wp-dependency-installer`
28 |
29 | The code installed via composer will automatically install and activate the EDD SL Updater plugin as a required dependency for any EDD SL Add-on.
30 |
31 | Additionally, you may need then need to run `composer update` prior to plugin distribution.
32 |
33 | The EDD SL Updater now uses a universal instantiation function that will correctly instantiate your plugin or theme based upon the configuration array passed to the function.
34 |
35 | ### Plugin Updater Example
36 |
37 | The following is an example of how to instantiate the settings/updater from a plugin. It is run from the main plugin file. This will provide a settings page for the plugin too.
38 |
39 | ```php
40 | // Automatically install EDD SL Updater.
41 | require_once __DIR__ . '/vendor/autoload.php';
42 | \WP_Dependency_Installer::instance( __DIR__ )->run();
43 |
44 | // Loads the updater classes
45 | function prefix_plugin_updater() {
46 | $config = [
47 | 'type' => 'plugin', // Declare the type.
48 | 'file' => __FILE__,
49 | 'api_url' => 'http://eddstore.test', // Site where EDD SL store is located.
50 | 'item_name' => 'EDD Test Plugin', // Name of plugin.
51 | 'item_id' => 11, // ID of the product.
52 | 'version' => '1.0', // Current version number.
53 | 'author' => 'Andy Fragen', // Author of this plugin.
54 | 'beta' => false,
55 | 'license' => '', // Optional, if plugin handles license actions you can set license here.
56 | ];
57 | if ( class_exists( 'EDD\\Software_Licensing\\Updater\\Bootstrap' ) ) {
58 | ( new EDD\Software_Licensing\Updater\Init() )->run( $config );
59 | }
60 | }
61 | add_action( 'plugins_loaded', 'prefix_plugin_updater' );
62 | ```
63 |
64 | ### Theme Updater Example
65 |
66 | The following is an example of how to instantiate the settings/updater from a theme. It is run from the theme's `functions.php` file. This will provide a settings page for your theme too.
67 |
68 | ```php
69 | // Automatically install EDD SL Updater.
70 | require_once __DIR__ . '/vendor/autoload.php';
71 | \WP_Dependency_Installer::instance()->run( __DIR__ );
72 |
73 | // Loads the updater classes
74 | function prefix_theme_updater() {
75 | $config = [
76 | 'type' => 'theme', // Declare the type.
77 | 'api_url' => 'http://eddstore.test', // Site where EDD SL store is located.
78 | 'item_name' => 'EDD Test Theme', // Name of theme.
79 | 'item_id' => 27, // ID of the product.
80 | 'slug' => 'edd-test-theme', // Theme slug.
81 | 'version' => '1.0', // Current version of this theme.
82 | 'author' => 'Andy Fragen', // Author of this theme
83 | 'download_id' => '', // Optional, used for generating a license renewal link.
84 | 'renew_url' => '', // Optional, allows for a custom license renewal link.
85 | 'beta' => false, // Optional, set to true to opt into beta versions.
86 | 'license' => '', // Optional, if theme handles license actions you can set license here.
87 | ];
88 | if ( class_exists( 'EDD\\Software_Licensing\\Updater\\Bootstrap' ) ) {
89 | ( new EDD\Software_Licensing\Updater\Init() )->run( $config );
90 | }
91 | }
92 | add_action( 'after_setup_theme', 'prefix_theme_updater' );
93 | ```
94 |
95 | ### Updater Only
96 |
97 | If your plugin or theme creates and manages it's own settings you can simply activate only the updater by changing the init code line to the following.
98 |
99 | ```php
100 | ( new EDD\Software_Licensing\Updater\Init() )->updater( $config );
101 | ```
102 |
103 | You must save/get your plugin/theme license in an option with the format `get_option( $slug . '_license_key' );` **or** you can set a key/value pair in the config array to return the license by setting the optional `license` key in the config array. Something like the following.
104 |
105 | ```php
106 | 'license' => get_option( 'my-license-key '),
107 | ```
108 |
109 | ### Decoupled Translation Packs
110 |
111 | This added framework allows for decoupled language pack updates. For more complete instructions please refer to [translations-updater framework](https://github.com/afragen/translations-updater).
112 |
113 | The URI should point to a repository that contains the translations files. Refer to [GitHub Updater Translations](https://github.com/afragen/github-updater-translations) as an example. It is created using the [Language Pack Maker](https://github.com/afragen/language-pack-maker). The repo **must** be a public repo.
114 |
115 | You will need to add two key/value pairs to your EDD SL Add-on config array similar to the following,
116 |
117 | ```php
118 | 'git' => 'github',
119 | 'languages' => 'https://github.com//my-language-pack',
120 | ```
121 |
122 | ## Changelog
123 | [Changelog](./CHANGES.md)
124 |
--------------------------------------------------------------------------------
/src/Updater_Common.php:
--------------------------------------------------------------------------------
1 | sections ) && is_object( $data->sections ) ) {
32 | $data->sections = $this->convert_object_to_array( $data->sections );
33 | }
34 | if ( isset( $data->contributors ) && is_object( $data->contributors ) ) {
35 | $data->contributors = $this->convert_object_to_array( $data->contributors );
36 | }
37 | if ( isset( $data->banners ) && is_object( $data->banners ) ) {
38 | $data->banners = $this->convert_object_to_array( $data->banners );
39 | }
40 | if ( isset( $data->icons ) && is_object( $data->icons ) ) {
41 | $data->icons = $this->convert_object_to_array( $data->icons );
42 | }
43 |
44 | return $data;
45 | }
46 |
47 | /**
48 | * Convert some objects to arrays when injecting data into the update API.
49 | *
50 | * Some data like sections, banners, and icons are expected to be an associative array,
51 | * however due to the JSON decoding, they are objects. This method allows us to pass
52 | * in the object and return an associative array.
53 | *
54 | * @since 3.6.5
55 | *
56 | * @param stdClass $data Data to be converted.
57 | *
58 | * @return array
59 | */
60 | private function convert_object_to_array( $data ) {
61 | $new_data = [];
62 | foreach ( $data as $key => $value ) {
63 | $new_data[ $key ] = is_object( $value ) ? $this->convert_object_to_array( $value ) : $value;
64 | }
65 |
66 | return $new_data;
67 | }
68 |
69 | /**
70 | * Get cached version info.
71 | *
72 | * @param string $cache_key Cache key.
73 | *
74 | * @return string $cache['value']
75 | */
76 | protected function get_cached_version_info( $cache_key = '' ) {
77 | if ( empty( $cache_key ) ) {
78 | $cache_key = $this->cache_key;
79 | }
80 |
81 | $cache = get_site_option( $cache_key );
82 |
83 | if ( empty( $cache['timeout'] ) || time() > $cache['timeout'] ) {
84 | return false; // Cache is expired.
85 | }
86 |
87 | if ( empty( $cache['value'] ) ) {
88 | return false; // Ensure there is some useful data.
89 | }
90 |
91 | // We need to turn the icons into an array, thanks to WP Core forcing these into an object at some point.
92 | $cache['value'] = json_decode( $cache['value'] );
93 | if ( ! empty( $cache['value']->icons ) ) {
94 | $cache['value']->icons = (array) $cache['value']->icons;
95 | }
96 |
97 | return $cache['value'];
98 | }
99 |
100 | /**
101 | * Set version info cache.
102 | *
103 | * @param string $value Cache value.
104 | * @param string $cache_key Cache key.
105 | *
106 | * @return bool|void
107 | */
108 | protected function set_version_info_cache( $value = '', $cache_key = '' ) {
109 | if ( empty( $value ) ) {
110 | return false;
111 | }
112 | if ( empty( $cache_key ) ) {
113 | $cache_key = $this->cache_key;
114 | }
115 |
116 | $data = [
117 | 'timeout' => strtotime( '+12 hours', time() ),
118 | 'value' => json_encode( $value ),
119 | ];
120 |
121 | update_site_option( $cache_key, $data );
122 | }
123 |
124 | /**
125 | * Get repo API data from store.
126 | * Save to cache.
127 | *
128 | * @param string $type (plugin|theme).
129 | *
130 | * @return \stdClass
131 | */
132 | protected function get_repo_api_data( $type ) {
133 | $file = 'plugin' === $type ? $this->file : $this->slug;
134 | $version_info = $this->get_cached_version_info();
135 |
136 | if ( ! $version_info ) {
137 | $version_info = $this->api_request(
138 | [
139 | 'edd_action' => 'get_version',
140 | 'license' => $this->license,
141 | 'name' => $this->item_name,
142 | 'slug' => $this->slug,
143 | 'version' => $this->version,
144 | 'author' => $this->author,
145 | 'beta' => $this->beta,
146 | ]
147 | );
148 | }
149 |
150 | if ( ! \is_object( $version_info ) ) {
151 | return false;
152 | }
153 |
154 | $version_info = $this->convert_sections_to_array( $version_info );
155 | $this->set_version_info_cache( $version_info );
156 |
157 | // Make sure the plugin|theme property is set.
158 | $version_info->{$type} = $file;
159 |
160 | // Add for auto update link, WP 5.5.
161 | $version_info->{'update-available'} = true;
162 |
163 | return $version_info;
164 | }
165 |
166 | /**
167 | * Check for Updates at the defined API endpoint and modify the update transient.
168 | *
169 | * // TODO: figure out what to do with $this->wp_override.
170 | *
171 | * @param array $transient Update transient.
172 | * @return array Modified update transienet with custom data.
173 | */
174 | public function update_transient( $transient ) {
175 | // needed to fix PHP 7.4 warning.
176 | if ( ! \is_object( $transient ) ) {
177 | $transient = new \stdClass();
178 | }
179 |
180 | $type = $this instanceof Plugin_Updater ? 'plugin' : null;
181 | $type = $this instanceof Theme_Updater ? 'theme' : $type;
182 | $file = 'plugin' === $type ? $this->file : $this->slug;
183 | $current = $this->get_repo_api_data( $type );
184 |
185 | if ( ! $current ) {
186 | return $transient;
187 | }
188 |
189 | // If there is no valid license key status, don't allow updates.
190 | if ( version_compare( $this->version, $current->new_version, '<' )
191 | && 'valid' === get_site_option( $this->slug . '_license_key_status', false )
192 | ) {
193 | $transient->response[ $file ] = 'plugin' === $type ? $current : (array) $current;
194 | } else {
195 | $transient->no_update[ $file ] = 'plugin' === $type ? $current : (array) $current;
196 | }
197 |
198 | return $transient;
199 | }
200 | }
201 |
--------------------------------------------------------------------------------
/vendor/composer/InstalledVersions.php:
--------------------------------------------------------------------------------
1 |
27 | array (
28 | 'pretty_version' => 'dev-develop',
29 | 'version' => 'dev-develop',
30 | 'aliases' =>
31 | array (
32 | ),
33 | 'reference' => '3ce34e7b89473dda7c4fce4533a6761e3b37e058',
34 | 'name' => 'afragen/edd-sl-updater',
35 | ),
36 | 'versions' =>
37 | array (
38 | 'afragen/edd-sl-updater' =>
39 | array (
40 | 'pretty_version' => 'dev-develop',
41 | 'version' => 'dev-develop',
42 | 'aliases' =>
43 | array (
44 | ),
45 | 'reference' => '3ce34e7b89473dda7c4fce4533a6761e3b37e058',
46 | ),
47 | 'afragen/translations-updater' =>
48 | array (
49 | 'pretty_version' => 'dev-master',
50 | 'version' => 'dev-master',
51 | 'aliases' =>
52 | array (
53 | ),
54 | 'reference' => '93cd3ad14f59e435f0cfa915c6971126095a29f1',
55 | ),
56 | ),
57 | );
58 | private static $canGetVendors;
59 | private static $installedByVendor = array();
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | public static function getInstalledPackages()
68 | {
69 | $packages = array();
70 | foreach (self::getInstalled() as $installed) {
71 | $packages[] = array_keys($installed['versions']);
72 | }
73 |
74 |
75 | if (1 === \count($packages)) {
76 | return $packages[0];
77 | }
78 |
79 | return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
80 | }
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 | public static function isInstalled($packageName)
91 | {
92 | foreach (self::getInstalled() as $installed) {
93 | if (isset($installed['versions'][$packageName])) {
94 | return true;
95 | }
96 | }
97 |
98 | return false;
99 | }
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 | public static function satisfies(VersionParser $parser, $packageName, $constraint)
115 | {
116 | $constraint = $parser->parseConstraints($constraint);
117 | $provided = $parser->parseConstraints(self::getVersionRanges($packageName));
118 |
119 | return $provided->matches($constraint);
120 | }
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 | public static function getVersionRanges($packageName)
132 | {
133 | foreach (self::getInstalled() as $installed) {
134 | if (!isset($installed['versions'][$packageName])) {
135 | continue;
136 | }
137 |
138 | $ranges = array();
139 | if (isset($installed['versions'][$packageName]['pretty_version'])) {
140 | $ranges[] = $installed['versions'][$packageName]['pretty_version'];
141 | }
142 | if (array_key_exists('aliases', $installed['versions'][$packageName])) {
143 | $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
144 | }
145 | if (array_key_exists('replaced', $installed['versions'][$packageName])) {
146 | $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
147 | }
148 | if (array_key_exists('provided', $installed['versions'][$packageName])) {
149 | $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
150 | }
151 |
152 | return implode(' || ', $ranges);
153 | }
154 |
155 | throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
156 | }
157 |
158 |
159 |
160 |
161 |
162 | public static function getVersion($packageName)
163 | {
164 | foreach (self::getInstalled() as $installed) {
165 | if (!isset($installed['versions'][$packageName])) {
166 | continue;
167 | }
168 |
169 | if (!isset($installed['versions'][$packageName]['version'])) {
170 | return null;
171 | }
172 |
173 | return $installed['versions'][$packageName]['version'];
174 | }
175 |
176 | throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
177 | }
178 |
179 |
180 |
181 |
182 |
183 | public static function getPrettyVersion($packageName)
184 | {
185 | foreach (self::getInstalled() as $installed) {
186 | if (!isset($installed['versions'][$packageName])) {
187 | continue;
188 | }
189 |
190 | if (!isset($installed['versions'][$packageName]['pretty_version'])) {
191 | return null;
192 | }
193 |
194 | return $installed['versions'][$packageName]['pretty_version'];
195 | }
196 |
197 | throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
198 | }
199 |
200 |
201 |
202 |
203 |
204 | public static function getReference($packageName)
205 | {
206 | foreach (self::getInstalled() as $installed) {
207 | if (!isset($installed['versions'][$packageName])) {
208 | continue;
209 | }
210 |
211 | if (!isset($installed['versions'][$packageName]['reference'])) {
212 | return null;
213 | }
214 |
215 | return $installed['versions'][$packageName]['reference'];
216 | }
217 |
218 | throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
219 | }
220 |
221 |
222 |
223 |
224 |
225 | public static function getRootPackage()
226 | {
227 | $installed = self::getInstalled();
228 |
229 | return $installed[0]['root'];
230 | }
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 | public static function getRawData()
239 | {
240 | return self::$installed;
241 | }
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 | public static function reload($data)
262 | {
263 | self::$installed = $data;
264 | self::$installedByVendor = array();
265 | }
266 |
267 |
268 |
269 |
270 | private static function getInstalled()
271 | {
272 | if (null === self::$canGetVendors) {
273 | self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
274 | }
275 |
276 | $installed = array();
277 |
278 | if (self::$canGetVendors) {
279 | foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
280 | if (isset(self::$installedByVendor[$vendorDir])) {
281 | $installed[] = self::$installedByVendor[$vendorDir];
282 | } elseif (is_file($vendorDir.'/composer/installed.php')) {
283 | $installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
284 | }
285 | }
286 | }
287 |
288 | $installed[] = self::$installed;
289 |
290 | return $installed;
291 | }
292 | }
293 |
--------------------------------------------------------------------------------
/src/License_Actions.php:
--------------------------------------------------------------------------------
1 | slug . '_license_activate' ] ) ) {
30 | if ( check_admin_referer( $this->slug . '_nonce', $this->slug . '_nonce' ) ) {
31 | $this->activate_license();
32 | }
33 | }
34 |
35 | if ( isset( $_POST[ $this->slug . '_license_deactivate' ] ) ) {
36 | if ( check_admin_referer( $this->slug . '_nonce', $this->slug . '_nonce' ) ) {
37 | $this->deactivate_license();
38 | }
39 | }
40 | }
41 |
42 | /**
43 | * Activate the license.
44 | *
45 | * Listen for our activate button to be clicked.
46 | * Exit early if we didn't click the Activate button.
47 | *
48 | * @since 1.0.0
49 | */
50 | public function activate_license() {
51 | if ( ! isset( $_POST[ $this->slug . '_license_activate' ] ) ) {
52 | return;
53 | }
54 | // run a quick security check.
55 | if ( ! check_admin_referer( $this->slug . '_nonce', $this->slug . '_nonce' ) ) {
56 | return;
57 | }
58 |
59 | // Data to send in our API request.
60 | $api_params = [
61 | 'edd_action' => 'activate_license',
62 | 'license' => $this->license,
63 | 'item_name' => rawurlencode( $this->item_name ), // the name of our product in EDD.
64 | 'item_id' => $this->item_id,
65 | 'url' => home_url(),
66 | 'environment' => function_exists( 'wp_get_environment_type' ) ? wp_get_environment_type() : false,
67 | ];
68 |
69 | add_filter( 'edd_sl_api_request_verify_ssl', '__return_false' );
70 | $license_data = $this->get_api_response( $this->api_url, $api_params );
71 |
72 | if ( ! $license_data->success && isset( $license_data->error ) ) {
73 | switch ( $license_data->error ) {
74 | case 'expired':
75 | $message = sprintf(
76 | $this->strings['license-key-expired-%s'],
77 | date_i18n( get_site_option( 'date_format' ), strtotime( $license_data->expires, time() ) )
78 | );
79 | break;
80 | case 'disabled':
81 | case 'revoked':
82 | $message = $this->strings['license-key-is-disabled'];
83 | break;
84 | case 'missing':
85 | $message = $this->strings['status-invalid'];
86 | break;
87 | case 'invalid':
88 | case 'site_inactive':
89 | $this->strings['license-inactive-url'];
90 | break;
91 | case 'item_name_mismatch':
92 | $message = sprintf( $this->strings['item-name-mismatch-%s'], $this->item_name );
93 | break;
94 | case 'no_activations_left':
95 | $message = $this->strings['license-activation-limit'];
96 | break;
97 | default:
98 | $message = $this->strings['error'];
99 | break;
100 | }
101 | }
102 | if ( isset( $license_data, $license_data->license ) ) {
103 | update_site_option( $this->slug . '_license_key_status', $license_data->license );
104 | delete_transient( $this->slug . '_license_message' );
105 | }
106 | if ( ! empty( $message ) ) {
107 | $error_data['slug'] = $this->slug;
108 | $error_data['success'] = false;
109 | $error_data['error_code'] =
110 | /* translators: %s: item name */
111 | sprintf( esc_attr__( 'activate_license-%s', 'edd-sl-updater' ), $this->data['item_name'] );
112 | $error_data['error_message'] = esc_html( $message );
113 | } else {
114 | $error_data = null;
115 | }
116 | $this->redirect( $error_data );
117 | }
118 |
119 | /**
120 | * Deactivate the license.
121 | *
122 | * Listen for our deactivate button to be clicked.
123 | * Exit early if we didn't click the deactivate button.
124 | *
125 | * @since 1.0.0
126 | */
127 | public function deactivate_license() {
128 | if ( ! isset( $_POST[ $this->slug . '_license_deactivate' ] ) ) {
129 | return;
130 | }
131 | // Run a quick security check.
132 | if ( ! check_admin_referer( $this->slug . '_nonce', $this->slug . '_nonce' ) ) {
133 | return;
134 | }
135 |
136 | // data to send in our API request.
137 | $api_params = [
138 | 'edd_action' => 'deactivate_license',
139 | 'license' => $this->license,
140 | 'item_name' => rawurlencode( $this->item_name ), // the name of our product in EDD.
141 | 'item_id' => $this->item_id,
142 | 'url' => home_url(),
143 | 'environment' => function_exists( 'wp_get_environment_type' ) ? wp_get_environment_type() : false,
144 | ];
145 |
146 | // Call the custom API.
147 | add_filter( 'edd_sl_api_request_verify_ssl', '__return_false' );
148 | $license_data = $this->get_api_response( $this->api_url, $api_params );
149 |
150 | // $license_data->license will be either "deactivated" or "failed".
151 | if ( $license_data->success && property_exists( $license_data, 'error' ) ) {
152 | $message = $this->strings['error'];
153 | }
154 | if ( ! isset( $license_data->license ) || 'deactivated' === $license_data->license ) {
155 | delete_site_option( $this->slug . '_license_key_status' );
156 | delete_transient( $this->slug . '_license_message' );
157 | }
158 | if ( ! empty( $message ) ) {
159 | $error_data['slug'] = $this->slug;
160 | $error_data['success'] = false;
161 | $error_data['error_code'] =
162 | /* translators: %s: item name */
163 | sprintf( esc_attr__( 'activate_license-%s', 'edd-sl-updater' ), $this->data['item_name'] );
164 | $error_data['error_message'] = esc_html( $message );
165 | } else {
166 | $error_data = null;
167 | }
168 | $this->redirect( $error_data );
169 | }
170 |
171 | /**
172 | * Get default strings.
173 | *
174 | * @return array $default_strings
175 | */
176 | public function get_strings() {
177 | $default_strings = [
178 | 'theme-license' => __( 'Theme License', 'edd-sl-updater' ),
179 | 'plugin-license' => __( 'Plugin License', 'edd-sl-updater' ),
180 | 'enter-key' => __( 'Enter your license key.', 'edd-sl-updater' ),
181 | 'license-key' => __( 'License Key', 'edd-sl-updater' ),
182 | 'license-action' => __( 'License Action', 'edd-sl-updater' ),
183 | 'deactivate-license' => __( 'Deactivate License', 'edd-sl-updater' ),
184 | 'activate-license' => __( 'Activate License', 'edd-sl-updater' ),
185 | 'status-unknown' => __( 'License status is unknown.', 'edd-sl-updater' ),
186 | 'status-invalid' => __( 'Invalid License.', 'edd-sl-updater' ),
187 | /* translators: %s: item name */
188 | 'item-name-mismatch-%s' => __( 'This appears to be an invalid license key for %s.', 'edd-sl-updater' ),
189 | 'renew' => __( 'Renew?', 'edd-sl-updater' ),
190 | 'unlimited' => __( 'unlimited', 'edd-sl-updater' ),
191 | 'license-key-is-active' => __( 'License key is active.', 'edd-sl-updater' ),
192 | /* translators: %s: expiration date */
193 | 'expires%s' => __( 'Expires %s.', 'edd-sl-updater' ),
194 | 'expires-never' => __( 'Lifetime License.', 'edd-sl-updater' ),
195 | /* translators: %1: number of sites activated, %2: total number of sites activated */
196 | '%1$s/%2$-sites' => __( 'You have %1$s / %2$s sites activated.', 'edd-sl-updater' ),
197 | /* translators: %s: expiration date */
198 | 'license-key-expired-%s' => __( 'License key expired on %s.', 'edd-sl-updater' ),
199 | 'license-key-expired' => __( 'License key has expired.', 'edd-sl-updater' ),
200 | 'license-keys-do-not-match' => __( 'License keys do not match.', 'edd-sl-updater' ),
201 | 'license-is-inactive' => __( 'License is inactive.', 'edd-sl-updater' ),
202 | 'license-key-is-disabled' => __( 'License key is disabled.', 'edd-sl-updater' ),
203 | 'license-inactive-url' => __( 'Your license is not active for this URL.', 'edd-sl-updater' ),
204 | 'site-is-inactive' => __( 'Site is inactive.', 'edd-sl-updater' ),
205 | 'license-status-unknown' => __( 'License status is unknown.', 'edd-sl-updater' ),
206 | 'license-activation-limit' => __( 'Your license key has reached its activation limit.', 'edd-sl-updater' ),
207 | 'update-notice' => __( "Updating this theme will lose any customizations you have made. 'Cancel' to stop, 'OK' to update.", 'edd-sl-updater' ),
208 | /* translators: %1: Name, %2: new version, %3: URL, %4: link title, %5: URL, %6: opening tag */
209 | 'update-available' => __( '%1$s %2$s is available. Check out what\'s new or update now.', 'edd-sl-updater' ),
210 | 'error' => __( 'An error occurred, please try again.', 'edd-sl-updater' ),
211 | ];
212 |
213 | /**
214 | * Filter the default strings.
215 | *
216 | * @since 1.0.0
217 | *
218 | * @param array $default_strings Array of default strings for theme updater.
219 | */
220 | return apply_filters( 'edd_sl_updater_strings', $default_strings );
221 | }
222 |
223 | /**
224 | * Create admin notice for errors.
225 | *
226 | * @return void
227 | */
228 | public function show_error() {
229 | $error_data = false;
230 | if ( $this instanceof Plugin_Updater_Admin ) {
231 | $error_data = get_transient( 'sl_plugin_activation' );
232 | }
233 | if ( $this instanceof Theme_Updater_Admin ) {
234 | $error_data = get_transient( 'sl_theme_activation' );
235 | }
236 | if ( ! $error_data || $this->slug !== $error_data['slug'] ) {
237 | return;
238 | }
239 | echo '';
240 | printf(
241 | /* translators: %1: error code, %2: error message */
242 | esc_html__( 'EDD SL - %1$s: %2$s' ),
243 | esc_attr( $error_data['error_code'] ),
244 | esc_html( $error_data['error_message'] )
245 | );
246 | echo '
';
247 | }
248 | }
249 |
--------------------------------------------------------------------------------
/src/API_Common.php:
--------------------------------------------------------------------------------
1 | 15,
35 | 'sslverify' => $verify_ssl,
36 | 'body' => $api_params,
37 | ]
38 | );
39 |
40 | $code = (int) wp_remote_retrieve_response_code( $response );
41 |
42 | if ( is_wp_error( $response ) ) {
43 | $error_data['slug'] = $this->slug;
44 | $error_data['success'] = false;
45 | $error_data['error_code'] = __( 'WP_Error', 'edd-sl-updater' );
46 | $error_data['error_message'] = $response->get_error_message();
47 | $this->redirect( $error_data );
48 | }
49 |
50 | if ( 200 !== $code ) {
51 | $error_data['slug'] = $this->slug;
52 | $error_data['success'] = false;
53 | $error_data['error_code'] = __( 'HTTP Error Code', 'edd-sl-updater' );
54 | $error_data['error_message'] = $code;
55 | $this->redirect( $error_data );
56 | }
57 |
58 | $response = json_decode( wp_remote_retrieve_body( $response ) );
59 |
60 | if ( empty( $response ) ) {
61 | $response = new \stdClass();
62 | $response->success = false;
63 | }
64 |
65 | $response->success = isset( $response->success ) ? $response->success : true;
66 |
67 | return $response;
68 | }
69 |
70 | /**
71 | * Checks if license is valid and gets expire date.
72 | *
73 | * @since 1.0.0
74 | *
75 | * @param string $slug Plugin/theme slug.
76 | * @return string $message License status message.
77 | */
78 | public function check_license( $slug ) {
79 | $license = trim( get_site_option( $slug . '_license_key' ) );
80 |
81 | $api_params = [
82 | 'edd_action' => 'check_license',
83 | 'license' => $license,
84 | 'item_name' => rawurlencode( $this->item_name ),
85 | 'item_id' => $this->item_id,
86 | 'url' => home_url(),
87 | 'environment' => function_exists( 'wp_get_environment_type' ) ? wp_get_environment_type() : false,
88 | ];
89 |
90 | $license_data = $this->get_api_response( $this->api_url, $api_params );
91 |
92 | // If response doesn't include license data, return.
93 | if ( ! isset( $license_data->license ) ) {
94 | $message = $this->strings['license-status-unknown'];
95 |
96 | return $message;
97 | }
98 |
99 | // We need to update the license status at the same time the message isupdated.
100 | if ( $license_data && isset( $license_data->license ) ) {
101 | update_site_option( $slug . '_license_key_status', $license_data->license );
102 | }
103 |
104 | // Get expire date.
105 | $expires = false;
106 | if ( isset( $license_data->expires ) && 'lifetime' !== $license_data->expires ) {
107 | $expires = date_i18n( get_site_option( 'date_format' ), strtotime( $license_data->expires, time() ) );
108 | $renew_link = '' . esc_attr( $this->strings['renew'] ) . '';
109 | } elseif ( isset( $license_data->expires ) && 'lifetime' === $license_data->expires ) {
110 | $expires = 'lifetime';
111 | }
112 |
113 | // Get site counts.
114 | $site_count = property_exists( $license_data, 'site_count' ) ? $license_data->site_count : null;
115 | $license_limit = property_exists( $license_data, 'license_limit' ) ? $license_data->license_limit : null;
116 |
117 | // If unlimited.
118 | if ( 0 === $license_limit ) {
119 | $license_limit = $this->strings['unlimited'];
120 | }
121 |
122 | switch ( $license_data->license ) {
123 | case 'valid':
124 | $message = $this->strings['license-key-is-active'] . ' ';
125 | if ( isset( $expires ) ) {
126 | $message = 'lifetime' === $expires ? $message .= $this->strings['expires-never'] : $message .= sprintf( $this->strings['expires%s'], $expires ) . ' ';
127 | }
128 | if ( $site_count && $license_limit ) {
129 | $message .= sprintf( $this->strings['%1$s/%2$-sites'], $site_count, $license_limit );
130 | }
131 | break;
132 | case 'expired':
133 | $message = $expires ? sprintf( $this->strings['license-key-expired-%s'], $expires ) : $this->strings['license-key-expired'];
134 | $message .= $renew_link ? ' ' . $renew_link : null;
135 | break;
136 | case 'invalid':
137 | $message = $this->strings['license-keys-do-not-match'];
138 | break;
139 | case 'inactive':
140 | $message = $this->strings['license-is-inactive'];
141 | break;
142 | case 'disabled':
143 | $message = $this->strings['license-key-is-disabled'];
144 | break;
145 | case 'site_inactive':
146 | $message = $this->strings['site-is-inactive'];
147 | break;
148 | default:
149 | $message = $this->strings['license-status-unknown'];
150 | }
151 |
152 | return sanitize_text_field( $message );
153 | }
154 |
155 | /**
156 | * Constructs a renewal link.
157 | *
158 | * @since 1.0.0
159 | */
160 | public function get_renewal_link() {
161 | // If a renewal link was passed in the config, use that.
162 | if ( ! empty( $this->renew_url ) ) {
163 | return $this->renew_url;
164 | }
165 |
166 | // If download_id was passed in the config, a renewal link can be constructed.
167 | $license_key = trim( get_site_option( $this->slug . '_license_key', false ) );
168 | if ( ! empty( $this->download_id ) && $license_key ) {
169 | $url = esc_url( $this->api_url );
170 | $url .= '/checkout/?edd_license_key=' . $license_key . '&download_id=' . $this->download_id;
171 |
172 | return $url;
173 | }
174 |
175 | // Otherwise return the api_url.
176 | return $this->api_url;
177 | }
178 |
179 | /**
180 | * Redirect to where we came from.
181 | *
182 | * @param array $error_data Data for error notice.
183 | * Default is null.
184 | *
185 | * @return void
186 | */
187 | public function redirect( $error_data = null ) {
188 | $redirect_url = wp_nonce_url( admin_url( 'options-general.php' ) );
189 | $location = add_query_arg(
190 | [ 'page' => 'edd-sl-updater' ],
191 | $redirect_url
192 | );
193 |
194 | // Save error message data to very short transient.
195 | if ( ! $error_data['success'] ) {
196 | if ( $this instanceof Plugin_Updater_Admin ) {
197 | set_transient( 'sl_plugin_activation', $error_data, 10 );
198 | }
199 | if ( $this instanceof Theme_Updater_Admin ) {
200 | set_transient( 'sl_theme_activation', $error_data, 10 );
201 | }
202 | }
203 |
204 | wp_safe_redirect( $location );
205 | exit();
206 | }
207 |
208 | /**
209 | * Disable SSL verification in order to prevent download update failures.
210 | *
211 | * @param array $args Array of HTTP args.
212 | * @param string $url URL.
213 | * @return object $array
214 | */
215 | public function http_request_args( $args, $url ) {
216 | if ( false !== strpos( $url, 'https://' ) && strpos( $url, 'edd_action=package_download' ) ) {
217 | $args['sslverify'] = $this->verify_ssl();
218 | }
219 |
220 | return $args;
221 | }
222 |
223 | /**
224 | * Returns if the SSL of the store should be verified.
225 | *
226 | * @since 1.6.13
227 | * @return bool
228 | */
229 | protected function verify_ssl() {
230 | return (bool) apply_filters( 'edd_sl_api_request_verify_ssl', true, $this );
231 | }
232 |
233 | /**
234 | * Calls the API and, if successful, returns the object delivered by the API.
235 | *
236 | * @param array $data Parameters for the API action.
237 | * @return false|object
238 | */
239 | protected function api_request( $data ) {
240 | global $edd_plugin_url_available;
241 |
242 | // Do a quick status check on this domain if we haven't already checked it.
243 | $store_hash = md5( $this->api_url );
244 | if ( ! is_array( $edd_plugin_url_available ) || ! isset( $edd_plugin_url_available[ $store_hash ] ) ) {
245 | $test_url_parts = parse_url( $this->api_url );
246 |
247 | $scheme = ! empty( $test_url_parts['scheme'] ) ? $test_url_parts['scheme'] : 'http';
248 | $host = ! empty( $test_url_parts['host'] ) ? $test_url_parts['host'] : '';
249 | $port = ! empty( $test_url_parts['port'] ) ? ':' . $test_url_parts['port'] : '';
250 |
251 | if ( empty( $host ) ) {
252 | $edd_plugin_url_available[ $store_hash ] = false;
253 | } else {
254 | $test_url = $scheme . '://' . $host . $port;
255 | $response = wp_remote_get(
256 | $test_url,
257 | [
258 | 'timeout' => $this->health_check_timeout,
259 | 'sslverify' => $this->verify_ssl(),
260 | ]
261 | );
262 | $edd_plugin_url_available[ $store_hash ] = is_wp_error( $response ) ? false : true;
263 | }
264 | }
265 |
266 | if ( false === $edd_plugin_url_available[ $store_hash ] ) {
267 | return false;
268 | }
269 |
270 | $data = array_merge( $this->api_data, $data );
271 |
272 | if ( $this->slug !== $data['slug'] ) {
273 | return false;
274 | }
275 |
276 | /**
277 | * Plugins are not able to update from the store.
278 | * This would cause the store to go into maintence mode as the plugin
279 | * tries to update, likely resulting in a non-responsive site.
280 | *
281 | * @link https://github.com/easydigitaldownloads/easy-digital-downloads/issues/7168
282 | */
283 | if ( trailingslashit( home_url() ) === $this->api_url ) {
284 | return false; // Don't allow a plugin to ping itself.
285 | }
286 |
287 | $api_params = [
288 | 'edd_action' => 'get_version',
289 | 'license' => ! empty( $data['license'] ) ? $data['license'] : '',
290 | 'item_name' => isset( $data['name'] ) ? $data['name'] : false,
291 | 'item_id' => isset( $data['item_id'] ) ? $data['item_id'] : false,
292 | 'version' => isset( $data['version'] ) ? $data['version'] : false,
293 | 'slug' => $data['slug'],
294 | 'author' => $data['author'],
295 | 'url' => home_url(),
296 | 'beta' => ! empty( $data['beta'] ),
297 | ];
298 |
299 | $request = $this->get_api_response( $this->api_url, $api_params );
300 |
301 | if ( $request && isset( $request->sections ) ) {
302 | $request->sections = maybe_unserialize( $request->sections );
303 | } else {
304 | $request = false;
305 | }
306 |
307 | if ( $request && isset( $request->banners ) ) {
308 | $request->banners = maybe_unserialize( $request->banners );
309 | }
310 |
311 | if ( $request && isset( $request->icons ) ) {
312 | $request->icons = maybe_unserialize( $request->icons );
313 | }
314 |
315 | if ( ! empty( $request->sections ) ) {
316 | foreach ( $request->sections as $key => $section ) {
317 | $request->$key = (array) $section;
318 | }
319 | }
320 |
321 | return $request;
322 | }
323 |
324 | /**
325 | * Disable requests to wp.org repository for this theme.
326 | *
327 | * @since 1.0.0
328 | *
329 | * TODO: Is this necessary, if yes should we implement for plugins too?
330 | *
331 | * @param array $r An array of HTTP request arguments.
332 | * @param string $url The request URL.
333 | *
334 | * @return array
335 | */
336 | public function disable_wporg_request( $r, $url ) {
337 | // If it's not a theme update request, bail.
338 | if ( 0 !== strpos( $url, 'https://api.wordpress.org/themes/update-check/1.1/' ) ) {
339 | return $r;
340 | }
341 |
342 | // Decode the JSON response.
343 | $themes = json_decode( $r['body']['themes'] );
344 |
345 | // Remove the active parent and child themes from the check.
346 | $parent = get_option( 'template' );
347 | $child = get_option( 'stylesheet' );
348 | unset( $themes->themes->$parent, $themes->themes->$child );
349 |
350 | // Encode the updated JSON response.
351 | $r['body']['themes'] = wp_json_encode( $themes );
352 |
353 | return $r;
354 | }
355 | }
356 |
--------------------------------------------------------------------------------
/vendor/composer/ClassLoader.php:
--------------------------------------------------------------------------------
1 |
7 | * Jordi Boggiano
8 | *
9 | * For the full copyright and license information, please view the LICENSE
10 | * file that was distributed with this source code.
11 | */
12 |
13 | namespace Composer\Autoload;
14 |
15 | /**
16 | * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
17 | *
18 | * $loader = new \Composer\Autoload\ClassLoader();
19 | *
20 | * // register classes with namespaces
21 | * $loader->add('Symfony\Component', __DIR__.'/component');
22 | * $loader->add('Symfony', __DIR__.'/framework');
23 | *
24 | * // activate the autoloader
25 | * $loader->register();
26 | *
27 | * // to enable searching the include path (eg. for PEAR packages)
28 | * $loader->setUseIncludePath(true);
29 | *
30 | * In this example, if you try to use a class in the Symfony\Component
31 | * namespace or one of its children (Symfony\Component\Console for instance),
32 | * the autoloader will first look for the class under the component/
33 | * directory, and it will then fallback to the framework/ directory if not
34 | * found before giving up.
35 | *
36 | * This class is loosely based on the Symfony UniversalClassLoader.
37 | *
38 | * @author Fabien Potencier
39 | * @author Jordi Boggiano
40 | * @see https://www.php-fig.org/psr/psr-0/
41 | * @see https://www.php-fig.org/psr/psr-4/
42 | */
43 | class ClassLoader
44 | {
45 | private $vendorDir;
46 |
47 | // PSR-4
48 | private $prefixLengthsPsr4 = array();
49 | private $prefixDirsPsr4 = array();
50 | private $fallbackDirsPsr4 = array();
51 |
52 | // PSR-0
53 | private $prefixesPsr0 = array();
54 | private $fallbackDirsPsr0 = array();
55 |
56 | private $useIncludePath = false;
57 | private $classMap = array();
58 | private $classMapAuthoritative = false;
59 | private $missingClasses = array();
60 | private $apcuPrefix;
61 |
62 | private static $registeredLoaders = array();
63 |
64 | public function __construct($vendorDir = null)
65 | {
66 | $this->vendorDir = $vendorDir;
67 | }
68 |
69 | public function getPrefixes()
70 | {
71 | if (!empty($this->prefixesPsr0)) {
72 | return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
73 | }
74 |
75 | return array();
76 | }
77 |
78 | public function getPrefixesPsr4()
79 | {
80 | return $this->prefixDirsPsr4;
81 | }
82 |
83 | public function getFallbackDirs()
84 | {
85 | return $this->fallbackDirsPsr0;
86 | }
87 |
88 | public function getFallbackDirsPsr4()
89 | {
90 | return $this->fallbackDirsPsr4;
91 | }
92 |
93 | public function getClassMap()
94 | {
95 | return $this->classMap;
96 | }
97 |
98 | /**
99 | * @param array $classMap Class to filename map
100 | */
101 | public function addClassMap(array $classMap)
102 | {
103 | if ($this->classMap) {
104 | $this->classMap = array_merge($this->classMap, $classMap);
105 | } else {
106 | $this->classMap = $classMap;
107 | }
108 | }
109 |
110 | /**
111 | * Registers a set of PSR-0 directories for a given prefix, either
112 | * appending or prepending to the ones previously set for this prefix.
113 | *
114 | * @param string $prefix The prefix
115 | * @param array|string $paths The PSR-0 root directories
116 | * @param bool $prepend Whether to prepend the directories
117 | */
118 | public function add($prefix, $paths, $prepend = false)
119 | {
120 | if (!$prefix) {
121 | if ($prepend) {
122 | $this->fallbackDirsPsr0 = array_merge(
123 | (array) $paths,
124 | $this->fallbackDirsPsr0
125 | );
126 | } else {
127 | $this->fallbackDirsPsr0 = array_merge(
128 | $this->fallbackDirsPsr0,
129 | (array) $paths
130 | );
131 | }
132 |
133 | return;
134 | }
135 |
136 | $first = $prefix[0];
137 | if (!isset($this->prefixesPsr0[$first][$prefix])) {
138 | $this->prefixesPsr0[$first][$prefix] = (array) $paths;
139 |
140 | return;
141 | }
142 | if ($prepend) {
143 | $this->prefixesPsr0[$first][$prefix] = array_merge(
144 | (array) $paths,
145 | $this->prefixesPsr0[$first][$prefix]
146 | );
147 | } else {
148 | $this->prefixesPsr0[$first][$prefix] = array_merge(
149 | $this->prefixesPsr0[$first][$prefix],
150 | (array) $paths
151 | );
152 | }
153 | }
154 |
155 | /**
156 | * Registers a set of PSR-4 directories for a given namespace, either
157 | * appending or prepending to the ones previously set for this namespace.
158 | *
159 | * @param string $prefix The prefix/namespace, with trailing '\\'
160 | * @param array|string $paths The PSR-4 base directories
161 | * @param bool $prepend Whether to prepend the directories
162 | *
163 | * @throws \InvalidArgumentException
164 | */
165 | public function addPsr4($prefix, $paths, $prepend = false)
166 | {
167 | if (!$prefix) {
168 | // Register directories for the root namespace.
169 | if ($prepend) {
170 | $this->fallbackDirsPsr4 = array_merge(
171 | (array) $paths,
172 | $this->fallbackDirsPsr4
173 | );
174 | } else {
175 | $this->fallbackDirsPsr4 = array_merge(
176 | $this->fallbackDirsPsr4,
177 | (array) $paths
178 | );
179 | }
180 | } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
181 | // Register directories for a new namespace.
182 | $length = strlen($prefix);
183 | if ('\\' !== $prefix[$length - 1]) {
184 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
185 | }
186 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
187 | $this->prefixDirsPsr4[$prefix] = (array) $paths;
188 | } elseif ($prepend) {
189 | // Prepend directories for an already registered namespace.
190 | $this->prefixDirsPsr4[$prefix] = array_merge(
191 | (array) $paths,
192 | $this->prefixDirsPsr4[$prefix]
193 | );
194 | } else {
195 | // Append directories for an already registered namespace.
196 | $this->prefixDirsPsr4[$prefix] = array_merge(
197 | $this->prefixDirsPsr4[$prefix],
198 | (array) $paths
199 | );
200 | }
201 | }
202 |
203 | /**
204 | * Registers a set of PSR-0 directories for a given prefix,
205 | * replacing any others previously set for this prefix.
206 | *
207 | * @param string $prefix The prefix
208 | * @param array|string $paths The PSR-0 base directories
209 | */
210 | public function set($prefix, $paths)
211 | {
212 | if (!$prefix) {
213 | $this->fallbackDirsPsr0 = (array) $paths;
214 | } else {
215 | $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
216 | }
217 | }
218 |
219 | /**
220 | * Registers a set of PSR-4 directories for a given namespace,
221 | * replacing any others previously set for this namespace.
222 | *
223 | * @param string $prefix The prefix/namespace, with trailing '\\'
224 | * @param array|string $paths The PSR-4 base directories
225 | *
226 | * @throws \InvalidArgumentException
227 | */
228 | public function setPsr4($prefix, $paths)
229 | {
230 | if (!$prefix) {
231 | $this->fallbackDirsPsr4 = (array) $paths;
232 | } else {
233 | $length = strlen($prefix);
234 | if ('\\' !== $prefix[$length - 1]) {
235 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
236 | }
237 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
238 | $this->prefixDirsPsr4[$prefix] = (array) $paths;
239 | }
240 | }
241 |
242 | /**
243 | * Turns on searching the include path for class files.
244 | *
245 | * @param bool $useIncludePath
246 | */
247 | public function setUseIncludePath($useIncludePath)
248 | {
249 | $this->useIncludePath = $useIncludePath;
250 | }
251 |
252 | /**
253 | * Can be used to check if the autoloader uses the include path to check
254 | * for classes.
255 | *
256 | * @return bool
257 | */
258 | public function getUseIncludePath()
259 | {
260 | return $this->useIncludePath;
261 | }
262 |
263 | /**
264 | * Turns off searching the prefix and fallback directories for classes
265 | * that have not been registered with the class map.
266 | *
267 | * @param bool $classMapAuthoritative
268 | */
269 | public function setClassMapAuthoritative($classMapAuthoritative)
270 | {
271 | $this->classMapAuthoritative = $classMapAuthoritative;
272 | }
273 |
274 | /**
275 | * Should class lookup fail if not found in the current class map?
276 | *
277 | * @return bool
278 | */
279 | public function isClassMapAuthoritative()
280 | {
281 | return $this->classMapAuthoritative;
282 | }
283 |
284 | /**
285 | * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
286 | *
287 | * @param string|null $apcuPrefix
288 | */
289 | public function setApcuPrefix($apcuPrefix)
290 | {
291 | $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
292 | }
293 |
294 | /**
295 | * The APCu prefix in use, or null if APCu caching is not enabled.
296 | *
297 | * @return string|null
298 | */
299 | public function getApcuPrefix()
300 | {
301 | return $this->apcuPrefix;
302 | }
303 |
304 | /**
305 | * Registers this instance as an autoloader.
306 | *
307 | * @param bool $prepend Whether to prepend the autoloader or not
308 | */
309 | public function register($prepend = false)
310 | {
311 | spl_autoload_register(array($this, 'loadClass'), true, $prepend);
312 |
313 | if (null === $this->vendorDir) {
314 | return;
315 | }
316 |
317 | if ($prepend) {
318 | self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
319 | } else {
320 | unset(self::$registeredLoaders[$this->vendorDir]);
321 | self::$registeredLoaders[$this->vendorDir] = $this;
322 | }
323 | }
324 |
325 | /**
326 | * Unregisters this instance as an autoloader.
327 | */
328 | public function unregister()
329 | {
330 | spl_autoload_unregister(array($this, 'loadClass'));
331 |
332 | if (null !== $this->vendorDir) {
333 | unset(self::$registeredLoaders[$this->vendorDir]);
334 | }
335 | }
336 |
337 | /**
338 | * Loads the given class or interface.
339 | *
340 | * @param string $class The name of the class
341 | * @return bool|null True if loaded, null otherwise
342 | */
343 | public function loadClass($class)
344 | {
345 | if ($file = $this->findFile($class)) {
346 | includeFile($file);
347 |
348 | return true;
349 | }
350 | }
351 |
352 | /**
353 | * Finds the path to the file where the class is defined.
354 | *
355 | * @param string $class The name of the class
356 | *
357 | * @return string|false The path if found, false otherwise
358 | */
359 | public function findFile($class)
360 | {
361 | // class map lookup
362 | if (isset($this->classMap[$class])) {
363 | return $this->classMap[$class];
364 | }
365 | if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
366 | return false;
367 | }
368 | if (null !== $this->apcuPrefix) {
369 | $file = apcu_fetch($this->apcuPrefix.$class, $hit);
370 | if ($hit) {
371 | return $file;
372 | }
373 | }
374 |
375 | $file = $this->findFileWithExtension($class, '.php');
376 |
377 | // Search for Hack files if we are running on HHVM
378 | if (false === $file && defined('HHVM_VERSION')) {
379 | $file = $this->findFileWithExtension($class, '.hh');
380 | }
381 |
382 | if (null !== $this->apcuPrefix) {
383 | apcu_add($this->apcuPrefix.$class, $file);
384 | }
385 |
386 | if (false === $file) {
387 | // Remember that this class does not exist.
388 | $this->missingClasses[$class] = true;
389 | }
390 |
391 | return $file;
392 | }
393 |
394 | /**
395 | * Returns the currently registered loaders indexed by their corresponding vendor directories.
396 | *
397 | * @return self[]
398 | */
399 | public static function getRegisteredLoaders()
400 | {
401 | return self::$registeredLoaders;
402 | }
403 |
404 | private function findFileWithExtension($class, $ext)
405 | {
406 | // PSR-4 lookup
407 | $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
408 |
409 | $first = $class[0];
410 | if (isset($this->prefixLengthsPsr4[$first])) {
411 | $subPath = $class;
412 | while (false !== $lastPos = strrpos($subPath, '\\')) {
413 | $subPath = substr($subPath, 0, $lastPos);
414 | $search = $subPath . '\\';
415 | if (isset($this->prefixDirsPsr4[$search])) {
416 | $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
417 | foreach ($this->prefixDirsPsr4[$search] as $dir) {
418 | if (file_exists($file = $dir . $pathEnd)) {
419 | return $file;
420 | }
421 | }
422 | }
423 | }
424 | }
425 |
426 | // PSR-4 fallback dirs
427 | foreach ($this->fallbackDirsPsr4 as $dir) {
428 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
429 | return $file;
430 | }
431 | }
432 |
433 | // PSR-0 lookup
434 | if (false !== $pos = strrpos($class, '\\')) {
435 | // namespaced class name
436 | $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
437 | . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
438 | } else {
439 | // PEAR-like class name
440 | $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
441 | }
442 |
443 | if (isset($this->prefixesPsr0[$first])) {
444 | foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
445 | if (0 === strpos($class, $prefix)) {
446 | foreach ($dirs as $dir) {
447 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
448 | return $file;
449 | }
450 | }
451 | }
452 | }
453 | }
454 |
455 | // PSR-0 fallback dirs
456 | foreach ($this->fallbackDirsPsr0 as $dir) {
457 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
458 | return $file;
459 | }
460 | }
461 |
462 | // PSR-0 include paths.
463 | if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
464 | return $file;
465 | }
466 |
467 | return false;
468 | }
469 | }
470 |
471 | /**
472 | * Scope isolated include.
473 | *
474 | * Prevents access to $this/self from included files.
475 | */
476 | function includeFile($file)
477 | {
478 | include $file;
479 | }
480 |
--------------------------------------------------------------------------------