PluginProbe
Jooosi Fon – Use Custom Fonts, Google Fonts or Adobe Fonts / trunk
Jooosi Fon – Use Custom Fonts, Google Fonts or Adobe Fonts vtrunk
1.1.2 1.1.3 1.1.0 1.1.1 trunk 1.0.100 1.0.114 1.0.30 1.0.31 1.0.32 1.0.33 1.0.34 1.0.35 1.0.36 1.0.37 1.0.38 1.0.39 1.0.40 1.0.41 1.0.42 1.0.45 1.0.46 1.0.47 1.0.48 1.0.49 All 74 releases
yabe-webfont / src / Plugin.php

Plugin.php in Jooosi Fon – Use Custom Fonts, Google Fonts or Adobe Fonts trunk, at src/Plugin.php

354 lines 13.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /*
4 * This file is part of the Jooosi Fon package.
5 *
6 * (c) Joshua Gugun Siagian <suabahasa@gmail.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11 declare (strict_types=1);
12 namespace JooosiFon;
13
14 use EasyDigitalDownloads\Updater\Registry;
15 use Exception;
16 use JooosiFonDeps\JOOOSI_FON;
17 use JooosiFon\Admin\AdminPage;
18 use JooosiFon\Api\Router as ApiRouter;
19 use JooosiFon\Builder\Integration as BuilderIntegration;
20 use JooosiFon\Core\Cache;
21 use JooosiFon\Core\Runtime;
22 use JooosiFon\Licensing\Manager as LicenseManager;
23 use JooosiFon\Upgrade\LegacyRebrandUpgrade;
24 use JooosiFon\Utils\Common;
25 use JooosiFon\Utils\Debug;
26 use JooosiFon\Utils\Notice;
27 /**
28 * Manage the plugin lifecycle and provides a single point of entry to the plugin.
29 *
30 * @author Joshua Gugun Siagian <suabahasa@gmail.com>
31 * @todo Remove all legacy Yabe Webfont hook shims completely in Jooosi Fon 3.0.0.
32 */
33 final class Plugin
34 {
35 /**
36 * Easy Digital Downloads Software Licensing integration manager.
37 * Pro version only.
38 *
39 * @var LicenseManager|null
40 */
41 public $plugin_updater = null;
42 /**
43 * Stores the instance, implementing a Singleton pattern.
44 */
45 private static self $instance;
46 /**
47 * The Singleton's constructor should always be private to prevent direct
48 * construction calls with the `new` operator.
49 */
50 private function __construct()
51 {
52 }
53 /**
54 * Singletons should not be cloneable.
55 */
56 private function __clone()
57 {
58 }
59 /**
60 * Singletons should not be restorable from strings.
61 *
62 * @throws Exception Cannot unserialize a singleton.
63 */
64 public function __wakeup()
65 {
66 throw new Exception('Cannot unserialize a singleton.');
67 }
68 /**
69 * This is the static method that controls the access to the singleton
70 * instance. On the first run, it creates a singleton object and places it
71 * into the static property. On subsequent runs, it returns the client existing
72 * object stored in the static property.
73 */
74 public static function get_instance(): self
75 {
76 if (!isset(self::$instance)) {
77 self::$instance = new self();
78 }
79 return self::$instance;
80 }
81 public function boot_debug()
82 {
83 if (\WP_DEBUG === \false) {
84 return;
85 }
86 // if (class_exists(\Sentry\SentrySdk::class)) {
87 // }
88 // when php exits, call the shutdown function.
89 register_shutdown_function(static fn() => Debug::shutdown());
90 }
91 public function boot_migration()
92 {
93 do_action('a!jooosi/fon/plugin:boot_migration.start');
94 do_action_deprecated('a!yabe/webfont/plugin:boot_migration.start', [], '2.1.0', 'a!jooosi/fon/plugin:boot_migration.start');
95 /** @var wpdb $wpdb */
96 global $wpdb;
97 $wpdb->jooosi_fon_prefix = JOOOSI_FON::DB_TABLE_PREFIX;
98 new \JooosiFon\Migration();
99 do_action('a!jooosi/fon/plugin:boot_migration.end');
100 do_action_deprecated('a!yabe/webfont/plugin:boot_migration.end', [], '2.1.0', 'a!jooosi/fon/plugin:boot_migration.end');
101 }
102 /**
103 * Boot to the Plugin.
104 */
105 public function boot(): void
106 {
107 $this->migrate_legacy_options();
108 $this->boot_license_sdk();
109 do_action('a!jooosi/fon/plugins:boot_start');
110 do_action_deprecated('a!yabe/webfont/plugins:boot_start', [], '2.1.0', 'a!jooosi/fon/plugins:boot_start');
111 $this->boot_debug();
112 $this->boot_migration();
113 (new LegacyRebrandUpgrade())->run();
114 // (de)activation hooks.
115 register_activation_hook(JOOOSI_FON::FILE, function (): void {
116 $this->activate_plugin();
117 });
118 register_deactivation_hook(JOOOSI_FON::FILE, function (): void {
119 $this->deactivate_plugin();
120 });
121 // upgrade hooks.
122 add_action('upgrader_process_complete', function ($upgrader, $options): void {
123 if ($options['action'] === 'update' && $options['type'] === 'plugin') {
124 foreach ($options['plugins'] as $plugin) {
125 if ($plugin === plugin_basename(JOOOSI_FON::FILE)) {
126 $this->upgrade_plugin();
127 }
128 }
129 }
130 }, 10, 2);
131 new Cache();
132 new Runtime();
133 new BuilderIntegration();
134 new ApiRouter();
135 $this->maybe_update_plugin();
136 // admin hooks.
137 if (is_admin()) {
138 add_filter('plugin_action_links_' . plugin_basename(JOOOSI_FON::FILE), fn($links) => $this->plugin_action_links($links));
139 add_action('plugins_loaded', function (): void {
140 $this->plugins_loaded_admin();
141 }, 100);
142 new AdminPage();
143 do_action('a!jooosi/fon/plugins:boot_admin');
144 do_action_deprecated('a!yabe/webfont/plugins:boot_admin', [], '2.1.0', 'a!jooosi/fon/plugins:boot_admin');
145 }
146 do_action('a!jooosi/fon/plugins:boot_end');
147 do_action_deprecated('a!yabe/webfont/plugins:boot_end', [], '2.1.0', 'a!jooosi/fon/plugins:boot_end');
148 }
149 /**
150 * Handle the plugin's activation
151 */
152 public function activate_plugin(): void
153 {
154 do_action('a!jooosi/fon/plugins:activate_plugin_start');
155 do_action_deprecated('a!yabe/webfont/plugins:activate_plugin_start', [], '2.1.0', 'a!jooosi/fon/plugins:activate_plugin_start');
156 update_option(JOOOSI_FON::WP_OPTION . '_version', JOOOSI_FON::VERSION);
157 if ($this->plugin_updater instanceof LicenseManager) {
158 $this->maybe_embedded_license();
159 $this->plugin_updater->clear_cache();
160 }
161 delete_transient('jooosi_fon_scanned_apis_' . JOOOSI_FON::VERSION);
162 do_action('a!jooosi/fon/plugins:activate_plugin_end');
163 do_action_deprecated('a!yabe/webfont/plugins:activate_plugin_end', [], '2.1.0', 'a!jooosi/fon/plugins:activate_plugin_end');
164 }
165 /**
166 * Handle plugin's deactivation by (maybe) cleaning up after ourselves.
167 */
168 public function deactivate_plugin(): void
169 {
170 do_action('a!jooosi/fon/plugins:deactivate_plugin_start');
171 do_action_deprecated('a!yabe/webfont/plugins:deactivate_plugin_start', [], '2.1.0', 'a!jooosi/fon/plugins:deactivate_plugin_start');
172 // TODO: Add deactivation logic here.
173 do_action('a!jooosi/fon/plugins:deactivate_plugin_end');
174 do_action_deprecated('a!yabe/webfont/plugins:deactivate_plugin_end', [], '2.1.0', 'a!jooosi/fon/plugins:deactivate_plugin_end');
175 }
176 /**
177 * Handle the plugin's upgrade
178 */
179 public function upgrade_plugin(): void
180 {
181 do_action('a!jooosi/fon/plugins:upgrade_plugin_start');
182 do_action_deprecated('a!yabe/webfont/plugins:upgrade_plugin_start', [], '2.1.0', 'a!jooosi/fon/plugins:upgrade_plugin_start');
183 update_option(JOOOSI_FON::WP_OPTION . '_version', JOOOSI_FON::VERSION);
184 do_action('a!jooosi/fon/plugins:upgrade_plugin_end');
185 do_action_deprecated('a!yabe/webfont/plugins:upgrade_plugin_end', [], '2.1.0', 'a!jooosi/fon/plugins:upgrade_plugin_end');
186 }
187 /**
188 * Warm up the plugin for admin.
189 */
190 public function plugins_loaded_admin(): void
191 {
192 add_action('admin_notices', static function () {
193 $messages = Notice::get_lists();
194 if ($messages && is_array($messages)) {
195 foreach ($messages as $message) {
196 echo sprintf('<div class="notice notice-%s is-dismissible %s">%s</div>', esc_attr($message['status']), JOOOSI_FON::WP_OPTION, esc_html($message['message']));
197 }
198 }
199 }, 100);
200 }
201 /**
202 * Add plugin action links.
203 *
204 * @param array<string> $links
205 * @return array<string>
206 */
207 public function plugin_action_links(array $links): array
208 {
209 $base_url = AdminPage::get_page_url();
210 array_unshift($links, sprintf('<a href="%s">%s</a>', esc_url(sprintf('%s#/settings', $base_url)), esc_html__('Settings', 'jooosi-fon')));
211 array_unshift($links, sprintf('<a href="%s">%s</a>', esc_url(sprintf('%s#/fonts/index', $base_url)), esc_html__('Fonts', 'jooosi-fon')));
212 if (!self::is_pro_edition()) {
213 array_unshift($links, sprintf('<a href="%s" style="color:#067b34;font-weight:600;" target="_blank">%s</a>', esc_url(Common::plugin_data('PluginURI') . '?utm_source=WordPress&utm_campaign=liteplugin&utm_medium=plugin-action-links&utm_content=Upgrade#pricing'), esc_html__('Upgrade to Pro', 'jooosi-fon')));
214 }
215 return $links;
216 }
217 /**
218 * Get the initialized license manager.
219 * Pro version only.
220 *
221 * @return LicenseManager|null
222 */
223 public function maybe_update_plugin()
224 {
225 return $this->plugin_updater;
226 }
227 public static function is_pro_edition(): bool
228 {
229 return is_file(self::get_license_sdk_path());
230 }
231 /**
232 * Register the plugin with EDD's official Software Licensing SDK.
233 */
234 private function boot_license_sdk(): void
235 {
236 $sdk_path = self::get_license_sdk_path();
237 if (!is_file($sdk_path)) {
238 return;
239 }
240 $this->plugin_updater = new LicenseManager();
241 add_action('init', [$this->plugin_updater, 'boot_updater']);
242 add_action('edd_sl_sdk_registry', function ($registry): void {
243 $this->register_license_integration($registry);
244 });
245 require_once $sdk_path;
246 // Plugin activation can happen after the SDK initialization hooks ran.
247 if (did_action('after_setup_theme') && class_exists(Registry::class)) {
248 $this->register_license_integration(Registry::instance());
249 }
250 }
251 /**
252 * @param mixed $registry
253 */
254 private function register_license_integration($registry): void
255 {
256 if (!$registry instanceof Registry) {
257 return;
258 }
259 if (!$registry->offsetExists(LicenseManager::INTEGRATION_ID)) {
260 $registry->register(LicenseManager::get_integration_args());
261 }
262 $handler = $registry->offsetGet(LicenseManager::INTEGRATION_ID);
263 if (is_object($handler) && method_exists($handler, 'auto_updater')) {
264 // The SDK registry does not pass wp_override or beta through in
265 // version 1.0.3, so Manager boots the official updater directly.
266 remove_action('init', [$handler, 'auto_updater']);
267 }
268 }
269 private static function get_license_sdk_path(): string
270 {
271 return dirname(JOOOSI_FON::FILE) . '/vendor/easy-digital-downloads/edd-sl-sdk/edd-sl-sdk.php';
272 }
273 /**
274 * Check if the plugin distributed with an embedded license and activate the license.
275 * Pro version only.
276 */
277 private function maybe_embedded_license(): void
278 {
279 $license_file = dirname(JOOOSI_FON::FILE) . '/license-data.php';
280 if (!file_exists($license_file)) {
281 return;
282 }
283 require_once $license_file;
284 $const_names = [
285 'JOOOSI_FON_EMBEDDED_LICENSE_KEY_' . JOOOSI_FON::EDD_STORE['item_id'],
286 // TODO: Remove this legacy embedded-license constant in Jooosi Fon 3.0.0.
287 'ROSUA_EMBEDDED_LICENSE_KEY_' . JOOOSI_FON::EDD_STORE['item_id'],
288 ];
289 $const_name = null;
290 foreach ($const_names as $candidate) {
291 if (defined($candidate)) {
292 $const_name = $candidate;
293 break;
294 }
295 }
296 if ($const_name === null) {
297 return;
298 }
299 $license_key = constant($const_name);
300 update_option(JOOOSI_FON::WP_OPTION . '_license', ['key' => $license_key, 'opt_in_pre_release' => \false]);
301 unlink($license_file);
302 // activate the license.
303 $this->plugin_updater->activate((string) $license_key);
304 }
305 /**
306 * Copy persisted values to the rebranded option keys, then keep both names
307 * synchronized on upgraded installations. Fresh installs only use the new
308 * keys because no legacy option exists to activate this compatibility path.
309 *
310 * @todo Remove this legacy option migration completely in Jooosi Fon 3.0.0.
311 */
312 private function migrate_legacy_options(): void
313 {
314 $missing = new \stdClass();
315 $optionMap = [];
316 $hasLegacyOptions = \false;
317 foreach (['_version', '_options', '_license', '_notices'] as $suffix) {
318 $option = JOOOSI_FON::WP_OPTION . $suffix;
319 $legacyOption = JOOOSI_FON::LEGACY_WP_OPTION . $suffix;
320 $optionMap[$option] = $legacyOption;
321 $legacyValue = get_option($legacyOption, $missing);
322 if ($legacyValue === $missing) {
323 continue;
324 }
325 $hasLegacyOptions = \true;
326 if (get_option($option, $missing) === $missing) {
327 add_option($option, $legacyValue);
328 }
329 }
330 if (!$hasLegacyOptions) {
331 return;
332 }
333 $legacyOptionMap = array_flip($optionMap);
334 $syncing = \false;
335 $syncOption = static function (string $option, $value) use ($optionMap, $legacyOptionMap, &$syncing): void {
336 if ($syncing) {
337 return;
338 }
339 $target = $optionMap[$option] ?? $legacyOptionMap[$option] ?? null;
340 if ($target === null) {
341 return;
342 }
343 $syncing = \true;
344 try {
345 update_option($target, $value);
346 } finally {
347 $syncing = \false;
348 }
349 };
350 add_action('updated_option', static fn($option, $oldValue, $value) => $syncOption($option, $value), 10, 3);
351 add_action('added_option', static fn($option, $value) => $syncOption($option, $value), 10, 2);
352 }
353 }
354