PluginProbe
Buttonizer – Floating Menus, Sticky Buttons, & Popup Builder / 3.6.0
Buttonizer – Floating Menus, Sticky Buttons, & Popup Builder v3.6.0
3.6.0 3.5.0 trunk 1.0.10 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.6.1 1.0.7 1.0.8 1.0.9 1.1 1.1.1 1.2 1.3 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.5 1.5.1 All 110 releases
buttonizer-multifunctional-button / app / Migration / SourcePlugin.php

SourcePlugin.php in Buttonizer – Floating Menus, Sticky Buttons, & Popup Builder 3.6.0, at app/Migration/SourcePlugin.php

327 lines 8.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 * SOFTWARE LICENSE INFORMATION
4 *
5 * Copyright (c) 2017 Buttonizer, all rights reserved.
6 *
7 * This file is part of Buttonizer
8 *
9 * For detailed information regarding to the licensing of
10 * this software, please review the license.txt or visit:
11 * https://buttonizer.pro/license/
12 */
13
14 namespace Buttonizer\Migration;
15
16 # No script kiddies
17 defined('ABSPATH') or die('No script kiddies please!');
18
19 /**
20 * Describes a plugin whose users are being migrated into Buttonizer.
21 *
22 * Everything plugin-specific about a migration lives in one of these
23 * descriptors, so the migration machinery itself stays generic. New
24 * acquisitions are added to SourcePlugins::all() and nothing else changes.
25 */
26 class SourcePlugin
27 {
28 /**
29 * @var array Raw descriptor configuration.
30 */
31 private $config;
32
33 /**
34 * @var string|null Resolved plugin basename, cached per request.
35 */
36 private $resolvedBaseName = null;
37
38 /**
39 * @var bool Whether the fallback scan already ran this request.
40 */
41 private $deepScanned = false;
42
43 /**
44 * @param array $config Descriptor configuration.
45 */
46 public function __construct(array $config)
47 {
48 $this->config = $config;
49 }
50
51 /**
52 * Module identifier (e.g. "chat-button").
53 */
54 public function id(): string
55 {
56 return $this->config['id'];
57 }
58
59 /**
60 * Human readable product name, used in admin notices.
61 */
62 public function label(): string
63 {
64 return $this->config['label'];
65 }
66
67 /**
68 * What the plugin is called on a site still running its old system.
69 *
70 * The two systems label their own menu entries, and a user who never left
71 * the old one has never seen the other name. Falls back to the current
72 * name for a plugin that only ever had one.
73 */
74 public function legacyLabel(): string
75 {
76 return $this->config['legacy_label'] ?? $this->config['label'];
77 }
78
79 /**
80 * Option prefix used by the source plugin (e.g. "bz_contact_button").
81 */
82 public function optionPrefix(): string
83 {
84 return $this->config['option_prefix'];
85 }
86
87 /**
88 * Admin page slug of the source plugin (e.g. "bz_button_contact").
89 */
90 public function pageSlug(): string
91 {
92 return $this->config['page_slug'] ?? '';
93 }
94
95 /**
96 * Option holding the source plugin's API token.
97 */
98 public function tokenOption(): string
99 {
100 return $this->optionPrefix() . '_site_connection';
101 }
102
103 /**
104 * Option holding the source plugin's settings array.
105 */
106 public function settingsOption(): string
107 {
108 return $this->optionPrefix() . '_settings';
109 }
110
111 /**
112 * Option holding the source plugin's cached account data.
113 */
114 public function accountOption(): string
115 {
116 return $this->optionPrefix() . '_account';
117 }
118
119 /**
120 * Option holding the source plugin's own legacy flag, if it has one.
121 *
122 * @return string|null
123 */
124 public function legacyFlagOption()
125 {
126 return $this->config['legacy_flag_option'] ?? null;
127 }
128
129 /**
130 * Value of the legacy flag option that means "this site runs legacy".
131 */
132 public function legacyFlagValue(): string
133 {
134 return $this->config['legacy_flag_value'] ?? 'yes';
135 }
136
137 /**
138 * Options that only exist on installs still running the source plugin's
139 * own pre-Buttonizer (legacy) system. Used for detection and for backups.
140 *
141 * @return string[]
142 */
143 public function legacyOptions(): array
144 {
145 return $this->config['legacy_options'] ?? [];
146 }
147
148 /**
149 * Settings keys carried over from the source plugin into Buttonizer.
150 *
151 * These are copied on top of the connection itself (token / site_id), so
152 * behaviour the user configured in the source plugin survives the move.
153 *
154 * @return string[]
155 */
156 public function settingsToCarryOver(): array
157 {
158 return $this->config['settings_to_carry_over'] ?? [];
159 }
160
161 /**
162 * Every option name that must be snapshotted before touching anything.
163 *
164 * @return string[]
165 */
166 public function optionsToBackup(): array
167 {
168 $options = [
169 $this->tokenOption(),
170 $this->settingsOption(),
171 $this->accountOption(),
172 ];
173
174 if ($this->legacyFlagOption()) {
175 $options[] = $this->legacyFlagOption();
176 }
177
178 $options = array_merge($options, $this->legacyOptions(), $this->config['extra_options'] ?? []);
179
180 return array_values(array_unique($options));
181 }
182
183 /**
184 * Does this source plugin ship an embedded module?
185 *
186 * Cloud installs need no module — their data lives in the Buttonizer
187 * account, not in code. Only the source plugin's own pre-Buttonizer system
188 * has to be carried over verbatim.
189 */
190 public function hasModule(): bool
191 {
192 return !empty($this->config['module_bootstrap']) && file_exists($this->modulePath());
193 }
194
195 /**
196 * Absolute path to the module's bootstrap file.
197 */
198 public function modulePath(): string
199 {
200 return BUTTONIZER_DIR . '/modules/' . ($this->config['module_bootstrap'] ?? '');
201 }
202
203 /**
204 * Admin page the module registers for itself (e.g. "contact_vr").
205 */
206 public function modulePageSlug(): string
207 {
208 return $this->config['module_page_slug'] ?? '';
209 }
210
211 /**
212 * Class that fits the module into Buttonizer, if it needs one.
213 *
214 * @return string|null Fully qualified class name with an attach() method.
215 */
216 public function moduleAdapter()
217 {
218 $adapter = $this->config['module_adapter'] ?? null;
219
220 return $adapter && class_exists($adapter) ? $adapter : null;
221 }
222
223 /**
224 * Is the module's code already in memory?
225 *
226 * The source plugin declares the same classes and functions in the global
227 * namespace, so loading the module while it is still running is a fatal
228 * redeclaration error.
229 */
230 public function isModuleLoaded(): bool
231 {
232 $guardClass = $this->config['module_guard_class'] ?? null;
233
234 if ($guardClass && class_exists($guardClass, false)) {
235 return true;
236 }
237
238 $guardConstant = $this->config['module_guard_constant'] ?? null;
239
240 return $guardConstant ? defined($guardConstant) : false;
241 }
242
243 /**
244 * Possible plugin basenames for the source plugin.
245 *
246 * The folder name differs between the development checkout and the build
247 * published to wordpress.org, so the basename is resolved at runtime
248 * instead of being hardcoded.
249 *
250 * @return string[]
251 */
252 public function baseNameCandidates(): array
253 {
254 return $this->config['base_name_candidates'] ?? [];
255 }
256
257 /**
258 * Resolve the basename of the installed source plugin.
259 *
260 * Tries the known candidates first — two file_exists() calls. The fallback
261 * scan over every installed plugin is opt-in, because this runs on every
262 * admin request and get_plugins() walks the whole plugin directory.
263 *
264 * @param bool $deepScan Allow scanning installed plugins by text domain.
265 *
266 * @return string|null Basename, or null when the plugin is not installed.
267 */
268 public function resolveBaseName(bool $deepScan = false)
269 {
270 if ($this->resolvedBaseName) {
271 return $this->resolvedBaseName;
272 }
273
274 foreach ($this->baseNameCandidates() as $candidate) {
275 if (file_exists(WP_PLUGIN_DIR . '/' . $candidate)) {
276 return $this->resolvedBaseName = $candidate;
277 }
278 }
279
280 if (!$deepScan || $this->deepScanned) {
281 return null;
282 }
283
284 $this->deepScanned = true;
285 $textDomain = $this->config['text_domain'] ?? null;
286
287 if ($textDomain && function_exists('get_plugins')) {
288 foreach (get_plugins() as $baseName => $plugin) {
289 if (isset($plugin['TextDomain']) && $plugin['TextDomain'] === $textDomain) {
290 return $this->resolvedBaseName = $baseName;
291 }
292 }
293 }
294
295 return null;
296 }
297
298 /**
299 * Is the source plugin installed on this site?
300 *
301 * @param bool $deepScan Allow the fallback scan by text domain.
302 */
303 public function isInstalled(bool $deepScan = false): bool
304 {
305 return $this->resolveBaseName($deepScan) !== null;
306 }
307
308 /**
309 * Is the source plugin currently active?
310 */
311 public function isActive(): bool
312 {
313 $baseName = $this->resolveBaseName();
314
315 if (!$baseName) {
316 return false;
317 }
318
319 if (function_exists('is_plugin_active')) {
320 return is_plugin_active($baseName);
321 }
322
323 // plugin.php is not loaded yet, read the option directly
324 return in_array($baseName, (array) get_option('active_plugins', []), true);
325 }
326 }
327