PluginProbe
Site Reviews / trunk
Site Reviews vtrunk
8.3.1 8.3.0 8.2.2 8.2.1 8.2.0 8.1.0 8.0.13 8.0.12 8.0.11 trunk 1.2.2 2.17.1 3.5.4 4.7.0 5.25.1 6.11.8 7.0.10 7.0.11 7.0.12 7.0.13 7.0.14 7.0.15 7.0.16 7.0.17 7.0.18 All 54 releases
site-reviews / plugin / Application.php

Application.php in Site Reviews trunk, at plugin/Application.php

300 lines 11.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace GeminiLabs\SiteReviews;
4
5 use GeminiLabs\SiteReviews\Contracts\PluginContract;
6 use GeminiLabs\SiteReviews\Contracts\ShortcodeContract;
7 use GeminiLabs\SiteReviews\Database\OptionManager;
8 use GeminiLabs\SiteReviews\Defaults\PermissionDefaults;
9 use GeminiLabs\SiteReviews\Helpers\Arr;
10 use GeminiLabs\SiteReviews\Modules\Migrate;
11 use GeminiLabs\SiteReviews\Modules\Queue;
12
13 /**
14 * @property array $addons
15 * @property string $basename
16 * @property string $capability
17 * @property string $cron_event
18 * @property array $db_version
19 * @property array $defaults
20 * @property string $export_key
21 * @property string $file
22 * @property string $id
23 * @property string $languages
24 * @property string $name
25 * @property string $paged_handle
26 * @property string $paged_query_var
27 * @property string $post_type
28 * @property string $prefix
29 * @property array $session
30 * @property array $settings
31 * @property Arguments $storage
32 * @property string $taxonomy
33 * @property string $version
34 * @property string $testedTo;
35 */
36 final class Application extends Container implements PluginContract
37 {
38 use Plugin;
39 use Session;
40 use Storage;
41
42 public const DB_VERSION = '1.5';
43 public const EXPORT_KEY = '_glsr_export';
44 public const ID = 'site-reviews';
45 public const PAGED_HANDLE = 'pagination_request';
46 public const PAGED_QUERY_VAR = 'reviews-page'; // filtered
47 public const POST_TYPE = 'site-review';
48 public const PREFIX = 'glsr_';
49 public const TAXONOMY = 'site-review-category';
50
51 protected array $addons = [];
52 protected array $defaults;
53 protected array $migrationArgs = [];
54 protected string $name;
55 protected array $settings;
56
57 public function addon(string $addonId)
58 {
59 return $this->addons[$addonId] ?? null;
60 }
61
62 /**
63 * @param mixed ...$args
64 */
65 public function can(string $capability, ...$args): bool
66 {
67 return $this->make(Role::class)->can($capability, ...$args);
68 }
69
70 /**
71 * The default plugin settings.
72 * This is first triggered on "init:5" in MainController::onInit.
73 */
74 public function defaults(): array
75 {
76 if (empty($this->defaults)) {
77 $settings = $this->settings();
78 $defaults = array_combine(array_keys($settings), wp_list_pluck($settings, 'default'));
79 $defaults = wp_parse_args($defaults, [
80 'version' => '',
81 'version_upgraded_from' => '0.0.0',
82 ]);
83 $defaults = Arr::unflatten($defaults);
84 $defaults = $this->filterArray('settings/defaults', $defaults);
85 $this->defaults = $defaults;
86 }
87 return $this->defaults;
88 }
89
90 public function getPermission(string $page = '', string $tab = 'index'): string
91 {
92 $fallback = 'edit_posts';
93 $permissions = $this->make(PermissionDefaults::class)->defaults();
94 $permission = Arr::get($permissions, $page, $fallback);
95 if (is_array($permission)) {
96 $permission = Arr::get($permission, $tab, $fallback);
97 }
98 $capability = empty($permission) || !is_string($permission)
99 ? $fallback
100 : $permission;
101 return $this->make(Role::class)->capability($capability);
102 }
103
104 public function hasPermission(string $page = '', string $tab = 'index'): bool
105 {
106 $isAdminScreen = is_admin() || is_network_admin();
107 return !$isAdminScreen || $this->can($this->getPermission($page, $tab));
108 }
109
110 /**
111 * This is the entry point to the plugin, it runs before "plugins_loaded".
112 * If this is a new major version, settings are copied over here and a migration is run.
113 */
114 public function init(): void
115 {
116 if (wp_installing()) {
117 return;
118 }
119 $args = [];
120 // Ensure the custom database tables exist, this is needed in cases
121 // where the plugin has been updated instead of activated.
122 if (empty(get_option(static::PREFIX.'db_version'))
123 && false === get_transient(static::PREFIX.'install_cooldown')) {
124 set_transient(static::PREFIX.'install_cooldown', 1, 15 * \MINUTE_IN_SECONDS);
125 $this->make(Install::class)->run();
126 }
127 // If this is a new major version, copy over the previous version settings
128 if (empty(get_option(OptionManager::databaseKey()))) {
129 $previous = $this->make(OptionManager::class)->previous();
130 if (!empty($previous)) {
131 update_option(OptionManager::databaseKey(), $previous, true);
132 $args['settings'] = true;
133 }
134 }
135 // Force a plugin migration on database version upgrades
136 if (static::DB_VERSION !== get_option(static::PREFIX.'db_version')) {
137 $args['database'] = true;
138 }
139 if (!empty($args)) {
140 // a named method rather than a closure, so that a site owner or an
141 // addon can remove_action() it if the migration must not be queued
142 $this->migrationArgs = $args;
143 add_action('init', [$this, 'queueMigration']);
144 }
145 $this->make(Hooks::class)->run();
146 }
147
148 public function isAdmin(): bool
149 {
150 $isAdminScreen = is_admin() || is_network_admin();
151 return $isAdminScreen && !wp_doing_ajax();
152 }
153
154 /**
155 * This is triggered on init:5 by $this->settings().
156 *
157 * @param PluginContract|string $addon
158 */
159 public function license($addon): void
160 {
161 try {
162 $settings = $this->settings();
163 $reflection = new \ReflectionClass($addon);
164 $id = $reflection->getConstant('ID');
165 $licensed = $reflection->getConstant('LICENSED');
166 $name = $reflection->getConstant('NAME');
167 if (true !== $licensed || 2 !== count(array_filter([$id, $name]))) {
168 return;
169 }
170 $license = [
171 "settings.licenses.{$id}" => [
172 'default' => '',
173 'label' => $name,
174 'sanitizer' => 'text',
175 /* translators: %s: link to the License Keys page */
176 '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'),
177 glsr_premium_link('license-keys')
178 ),
179 'type' => 'secret',
180 ],
181 ];
182 $this->settings = array_merge($license, $settings);
183 } catch (\ReflectionException $e) {
184 // Fail silently
185 }
186 }
187
188 /**
189 * Queues the plugin migration that init() detected was needed.
190 * Hooked on "init" as a named method, so a site can remove_action() it.
191 */
192 public function queueMigration(): void
193 {
194 if (!empty($this->migrationArgs) && $this->make(Migrate::class)->canQueue()) {
195 $this->make(Queue::class)->once(time() + 15, 'queue/migration', $this->migrationArgs, true);
196 }
197 }
198
199 /**
200 * This is triggered on "plugins_loaded:-10" by "site-reviews/premium/register".
201 */
202 public function register(string $addon, ?PluginContract $host = null): void
203 {
204 try {
205 $reflection = new \ReflectionClass($addon); // make sure that the class exists
206 } catch (\ReflectionException $e) {
207 glsr_log()->error("Attempted to register an invalid addon [$addon]");
208 return;
209 }
210 $addonId = $reflection->getConstant('ID');
211 $file = dirname(dirname($reflection->getFileName()));
212 $file = trailingslashit($file).$addonId.'.php';
213 if (!file_exists($file)) {
214 if (!$host instanceof PluginContract) {
215 glsr_log()->error("Attempted to register an invalid addon [$addonId].");
216 return;
217 }
218 $file = $host->file; // hosted addon: the host's headers gate the whole bundle
219 }
220 $premium = glsr()->filterArray('site-reviews-premium', []);
221 if (in_array($addonId, $premium)
222 && !str_starts_with($reflection->getNamespaceName(), 'GeminiLabs\SiteReviews\Premium')) {
223 $this->append('site-reviews-premium', $addon);
224 return;
225 }
226 if (true === $reflection->getConstant('LICENSED')) {
227 $this->append('licensed', $addon, $addonId);
228 }
229 $pluginData = get_file_data($file, [
230 'glsr_version_required' => 'GLSR requires at least',
231 'glsr_version_unsupported' => 'GLSR unsupported version',
232 ], 'plugin');
233 if (version_compare($this->version, $pluginData['glsr_version_required'], '<')) {
234 return; // This addon requires a newer version of Site Reviews
235 }
236 if (version_compare($this->version, $pluginData['glsr_version_unsupported'], '>=')) {
237 return; // This addon requires an older version of Site Reviews
238 }
239 $this->addons[$addonId] = $addon;
240 $this->singleton($addon); // this goes first!
241 $parameters = [];
242 if ($host instanceof PluginContract) {
243 $parameters = ['host' => $host];
244 if ($host instanceof Addons\Addon) {
245 $host->markAsHost();
246 }
247 }
248 $instance = $this->make($addon, $parameters);
249 $this->alias($addon, $instance); // instances built with parameters are not auto-cached
250 $this->alias($addonId, $instance);
251 $instance->init();
252 $this->append('addons', $instance->version, $instance->id);
253 $this->discard('settings'); // recompose the settings view now that this addon's option is mounted
254 }
255
256 /**
257 * The plugin settings configuration.
258 * This is first triggered on "init:5" by MainController::onInit.
259 *
260 * @return mixed
261 */
262 public function settings(string $path = '')
263 {
264 if (empty($this->settings)) {
265 $settings = $this->config('settings');
266 $settings = $this->filterArray('settings', $settings);
267 array_walk($settings, function (&$setting) {
268 $setting = wp_parse_args($setting, [
269 'default' => '',
270 'sanitizer' => 'text',
271 ]);
272 });
273 $this->settings = $settings; // do this before adding license settings!
274 $licensedAddons = $this->retrieveAs('array', 'licensed', []);
275 array_walk($licensedAddons, fn ($addon) => $this->license($addon));
276 }
277 if (empty($path)) {
278 return $this->settings;
279 }
280 $settings = Arr::unflatten($this->settings);
281 return Arr::get($settings, $path);
282 }
283
284 public function shortcode(?string $shortcode): ?ShortcodeContract
285 {
286 if (empty($shortcode)) {
287 return null;
288 }
289 $shortcodes = glsr()->retrieveAs('array', 'shortcodes');
290 $className = $shortcodes[$shortcode] ?? '';
291 if (!class_exists($className)) {
292 return null;
293 }
294 if (!(new \ReflectionClass($className))->implementsInterface(ShortcodeContract::class)) {
295 return null;
296 }
297 return $this->make($className);
298 }
299 }
300