JezK
Edit File: Application.php
<?php namespace GeminiLabs\SiteReviews; use GeminiLabs\SiteReviews\Contracts\PluginContract; use GeminiLabs\SiteReviews\Contracts\ShortcodeContract; use GeminiLabs\SiteReviews\Database\OptionManager; use GeminiLabs\SiteReviews\Defaults\PermissionDefaults; use GeminiLabs\SiteReviews\Helpers\Arr; use GeminiLabs\SiteReviews\Modules\Migrate; use GeminiLabs\SiteReviews\Modules\Queue; /** * @property array $addons * @property string $basename * @property string $capability * @property string $cron_event * @property array $db_version * @property array $defaults * @property string $export_key * @property string $file * @property string $id * @property string $languages * @property string $name * @property string $paged_handle * @property string $paged_query_var * @property string $post_type * @property string $prefix * @property array $session * @property array $settings * @property Arguments $storage * @property string $taxonomy * @property string $version * @property string $testedTo; */ final class Application extends Container implements PluginContract { use Plugin; use Session; use Storage; public const DB_VERSION = '1.5'; public const EXPORT_KEY = '_glsr_export'; public const ID = 'site-reviews'; public const PAGED_HANDLE = 'pagination_request'; public const PAGED_QUERY_VAR = 'reviews-page'; // filtered public const POST_TYPE = 'site-review'; public const PREFIX = 'glsr_'; public const TAXONOMY = 'site-review-category'; protected array $addons = []; protected array $defaults; protected array $migrationArgs = []; protected string $name; protected array $settings; public function addon(string $addonId) { return $this->addons[$addonId] ?? null; } /** * @param mixed ...$args */ public function can(string $capability, ...$args): bool { return $this->make(Role::class)->can($capability, ...$args); } /** * The default plugin settings. * This is first triggered on "init:5" in MainController::onInit. */ public function defaults(): array { if (empty($this->defaults)) { $settings = $this->settings(); $defaults = array_combine(array_keys($settings), wp_list_pluck($settings, 'default')); $defaults = wp_parse_args($defaults, [ 'version' => '', 'version_upgraded_from' => '0.0.0', ]); $defaults = Arr::unflatten($defaults); $defaults = $this->filterArray('settings/defaults', $defaults); $this->defaults = $defaults; } return $this->defaults; } public function getPermission(string $page = '', string $tab = 'index'): string { $fallback = 'edit_posts'; $permissions = $this->make(PermissionDefaults::class)->defaults(); $permission = Arr::get($permissions, $page, $fallback); if (is_array($permission)) { $permission = Arr::get($permission, $tab, $fallback); } $capability = empty($permission) || !is_string($permission) ? $fallback : $permission; return $this->make(Role::class)->capability($capability); } public function hasPermission(string $page = '', string $tab = 'index'): bool { $isAdminScreen = is_admin() || is_network_admin(); return !$isAdminScreen || $this->can($this->getPermission($page, $tab)); } /** * This is the entry point to the plugin, it runs before "plugins_loaded". * If this is a new major version, settings are copied over here and a migration is run. */ public function init(): void { if (wp_installing()) { return; } $args = []; // Ensure the custom database tables exist, this is needed in cases // where the plugin has been updated instead of activated. if (empty(get_option(static::PREFIX.'db_version')) && false === get_transient(static::PREFIX.'install_cooldown')) { set_transient(static::PREFIX.'install_cooldown', 1, 15 * \MINUTE_IN_SECONDS); $this->make(Install::class)->run(); } // If this is a new major version, copy over the previous version settings if (empty(get_option(OptionManager::databaseKey()))) { $previous = $this->make(OptionManager::class)->previous(); if (!empty($previous)) { update_option(OptionManager::databaseKey(), $previous, true); $args['settings'] = true; } } // Force a plugin migration on database version upgrades if (static::DB_VERSION !== get_option(static::PREFIX.'db_version')) { $args['database'] = true; } if (!empty($args)) { // a named method rather than a closure, so that a site owner or an // addon can remove_action() it if the migration must not be queued $this->migrationArgs = $args; add_action('init', [$this, 'queueMigration']); } $this->make(Hooks::class)->run(); } public function isAdmin(): bool { $isAdminScreen = is_admin() || is_network_admin(); return $isAdminScreen && !wp_doing_ajax(); } /** * This is triggered on init:5 by $this->settings(). * * @param PluginContract|string $addon */ public function license($addon): void { try { $settings = $this->settings(); $reflection = new \ReflectionClass($addon); $id = $reflection->getConstant('ID'); $licensed = $reflection->getConstant('LICENSED'); $name = $reflection->getConstant('NAME'); if (true !== $licensed || 2 !== count(array_filter([$id, $name]))) { return; } $license = [ "settings.licenses.{$id}" => [ 'default' => '', 'label' => $name, 'sanitizer' => 'text', /* translators: %s: link to the License Keys page */ 'tooltip' => sprintf(_x('Enter the license key here. Your license can be found on the %s page of your Nifty Plugins account.', 'link to License Keys page (admin-text)', 'site-reviews'), glsr_premium_link('license-keys') ), 'type' => 'secret', ], ]; $this->settings = array_merge($license, $settings); } catch (\ReflectionException $e) { // Fail silently } } /** * Queues the plugin migration that init() detected was needed. * Hooked on "init" as a named method, so a site can remove_action() it. */ public function queueMigration(): void { if (!empty($this->migrationArgs) && $this->make(Migrate::class)->canQueue()) { $this->make(Queue::class)->once(time() + 15, 'queue/migration', $this->migrationArgs, true); } } /** * This is triggered on "plugins_loaded:-10" by "site-reviews/premium/register". */ public function register(string $addon, ?PluginContract $host = null): void { try { $reflection = new \ReflectionClass($addon); // make sure that the class exists } catch (\ReflectionException $e) { glsr_log()->error("Attempted to register an invalid addon [$addon]"); return; } $addonId = $reflection->getConstant('ID'); $file = dirname(dirname($reflection->getFileName())); $file = trailingslashit($file).$addonId.'.php'; if (!file_exists($file)) { if (!$host instanceof PluginContract) { glsr_log()->error("Attempted to register an invalid addon [$addonId]."); return; } $file = $host->file; // hosted addon: the host's headers gate the whole bundle } $premium = glsr()->filterArray('site-reviews-premium', []); if (in_array($addonId, $premium) && !str_starts_with($reflection->getNamespaceName(), 'GeminiLabs\SiteReviews\Premium')) { $this->append('site-reviews-premium', $addon); return; } if (true === $reflection->getConstant('LICENSED')) { $this->append('licensed', $addon, $addonId); } $pluginData = get_file_data($file, [ 'glsr_version_required' => 'GLSR requires at least', 'glsr_version_unsupported' => 'GLSR unsupported version', ], 'plugin'); if (version_compare($this->version, $pluginData['glsr_version_required'], '<')) { return; // This addon requires a newer version of Site Reviews } if (version_compare($this->version, $pluginData['glsr_version_unsupported'], '>=')) { return; // This addon requires an older version of Site Reviews } $this->addons[$addonId] = $addon; $this->singleton($addon); // this goes first! $parameters = []; if ($host instanceof PluginContract) { $parameters = ['host' => $host]; if ($host instanceof Addons\Addon) { $host->markAsHost(); } } $instance = $this->make($addon, $parameters); $this->alias($addon, $instance); // instances built with parameters are not auto-cached $this->alias($addonId, $instance); $instance->init(); $this->append('addons', $instance->version, $instance->id); $this->discard('settings'); // recompose the settings view now that this addon's option is mounted } /** * The plugin settings configuration. * This is first triggered on "init:5" by MainController::onInit. * * @return mixed */ public function settings(string $path = '') { if (empty($this->settings)) { $settings = $this->config('settings'); $settings = $this->filterArray('settings', $settings); array_walk($settings, function (&$setting) { $setting = wp_parse_args($setting, [ 'default' => '', 'sanitizer' => 'text', ]); }); $this->settings = $settings; // do this before adding license settings! $licensedAddons = $this->retrieveAs('array', 'licensed', []); array_walk($licensedAddons, fn ($addon) => $this->license($addon)); } if (empty($path)) { return $this->settings; } $settings = Arr::unflatten($this->settings); return Arr::get($settings, $path); } public function shortcode(?string $shortcode): ?ShortcodeContract { if (empty($shortcode)) { return null; } $shortcodes = glsr()->retrieveAs('array', 'shortcodes'); $className = $shortcodes[$shortcode] ?? ''; if (!class_exists($className)) { return null; } if (!(new \ReflectionClass($className))->implementsInterface(ShortcodeContract::class)) { return null; } return $this->make($className); } }