PluginProbe
Buttonizer – Floating Menus, Sticky Buttons, & Popup Builder / 1.5
Buttonizer – Floating Menus, Sticky Buttons, & Popup Builder v1.5
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 / freemius / includes / class-fs-storage.php

class-fs-storage.php in Buttonizer – Floating Menus, Sticky Buttons, & Popup Builder 1.5, at freemius/includes/class-fs-storage.php

523 lines 19.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package Freemius
4 * @copyright Copyright (c) 2015, Freemius, Inc.
5 * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6 * @since 1.2.3
7 */
8
9 if ( ! defined( 'ABSPATH' ) ) {
10 exit;
11 }
12
13 /**
14 * Class FS_Storage
15 *
16 * A wrapper class for handling network level and single site level storage.
17 *
18 * @property bool $is_network_activation
19 * @property int $network_install_blog_id
20 * @property object $sync_cron
21 */
22 class FS_Storage {
23 /**
24 * @var FS_Storage[]
25 */
26 private static $_instances = array();
27 /**
28 * @var FS_Key_Value_Storage Site level storage.
29 */
30 private $_storage;
31
32 /**
33 * @var FS_Key_Value_Storage Network level storage.
34 */
35 private $_network_storage;
36
37 /**
38 * @var string
39 */
40 private $_module_type;
41
42 /**
43 * @var string
44 */
45 private $_module_slug;
46
47 /**
48 * @var int The ID of the blog that is associated with the current site level options.
49 */
50 private $_blog_id = 0;
51
52 /**
53 * @var bool
54 */
55 private $_is_multisite;
56
57 /**
58 * @var bool
59 */
60 private $_is_network_active = false;
61
62 /**
63 * @var bool
64 */
65 private $_is_delegated_connection = false;
66
67 /**
68 * @var array {
69 * @key string Option name.
70 * @value int If 0 store on the network level. If 1, store on the network level only if module was network level activated. If 2, store on the network level only if network activated and NOT delegated the connection.
71 * }
72 */
73 private static $_NETWORK_OPTIONS_MAP;
74
75 /**
76 * @author Leo Fajardo (@leorw)
77 *
78 * @param string $module_type
79 * @param string $slug
80 *
81 * @return FS_Storage
82 */
83 static function instance( $module_type, $slug ) {
84 $key = $module_type . ':' . $slug;
85
86 if ( ! isset( self::$_instances[ $key ] ) ) {
87 self::$_instances[ $key ] = new FS_Storage( $module_type, $slug );
88 }
89
90 return self::$_instances[ $key ];
91 }
92
93 /**
94 * @author Leo Fajardo (@leorw)
95 *
96 * @param string $module_type
97 * @param string $slug
98 */
99 private function __construct( $module_type, $slug ) {
100 $this->_module_type = $module_type;
101 $this->_module_slug = $slug;
102 $this->_is_multisite = is_multisite();
103
104 if ( $this->_is_multisite ) {
105 $this->_blog_id = get_current_blog_id();
106 $this->_network_storage = FS_Key_Value_Storage::instance( $module_type . '_data', $slug, true );
107 }
108
109 $this->_storage = FS_Key_Value_Storage::instance( $module_type . '_data', $slug, $this->_blog_id );
110 }
111
112 /**
113 * Tells this storage wrapper class that the context plugin is network active. This flag will affect how values
114 * are retrieved/stored from/into the storage.
115 *
116 * @author Leo Fajardo (@leorw)
117 *
118 * @param bool $is_network_active
119 * @param bool $is_delegated_connection
120 */
121 function set_network_active( $is_network_active = true, $is_delegated_connection = false ) {
122 $this->_is_network_active = $is_network_active;
123 $this->_is_delegated_connection = $is_delegated_connection;
124 }
125
126 /**
127 * Switch the context of the site level storage manager.
128 *
129 * @author Vova Feldman (@svovaf)
130 * @since 2.0.0
131 *
132 * @param int $blog_id
133 */
134 function set_site_blog_context( $blog_id ) {
135 $this->_blog_id = $blog_id;
136
137 $this->_storage = $this->get_site_storage( $this->_blog_id );
138 }
139
140 /**
141 * @author Leo Fajardo (@leorw)
142 *
143 * @param string $key
144 * @param mixed $value
145 * @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_BINARY_MAP).
146 * @param bool $flush
147 */
148 function store( $key, $value, $network_level_or_blog_id = null, $flush = true ) {
149 if ( $this->should_use_network_storage( $key, $network_level_or_blog_id ) ) {
150 $this->_network_storage->store( $key, $value, $flush );
151 } else {
152 $storage = $this->get_site_storage( $network_level_or_blog_id );
153 $storage->store( $key, $value, $flush );
154 }
155 }
156
157 /**
158 * @author Leo Fajardo (@leorw)
159 *
160 * @param bool $store
161 * @param string[] $exceptions Set of keys to keep and not clear.
162 * @param int|null|bool $network_level_or_blog_id
163 */
164 function clear_all( $store = true, $exceptions = array(), $network_level_or_blog_id = null ) {
165 if ( ! $this->_is_multisite ||
166 false === $network_level_or_blog_id ||
167 is_null( $network_level_or_blog_id ) ||
168 is_numeric( $network_level_or_blog_id )
169 ) {
170 $storage = $this->get_site_storage( $network_level_or_blog_id );
171 $storage->clear_all( $store, $exceptions );
172 }
173
174 if ( $this->_is_multisite &&
175 ( true === $network_level_or_blog_id || is_null( $network_level_or_blog_id ) )
176 ) {
177 $this->_network_storage->clear_all( $store, $exceptions );
178 }
179 }
180
181 /**
182 * @author Leo Fajardo (@leorw)
183 *
184 * @param string $key
185 * @param bool $store
186 * @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_BINARY_MAP).
187 */
188 function remove( $key, $store = true, $network_level_or_blog_id = null ) {
189 if ( $this->should_use_network_storage( $key, $network_level_or_blog_id ) ) {
190 $this->_network_storage->remove( $key, $store );
191 } else {
192 $storage = $this->get_site_storage( $network_level_or_blog_id );
193 $storage->remove( $key, $store );
194 }
195 }
196
197 /**
198 * @author Leo Fajardo (@leorw)
199 *
200 * @param string $key
201 * @param mixed $default
202 * @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_BINARY_MAP).
203 *
204 * @return mixed
205 */
206 function get( $key, $default = false, $network_level_or_blog_id = null ) {
207 if ( $this->should_use_network_storage( $key, $network_level_or_blog_id ) ) {
208 return $this->_network_storage->get( $key, $default );
209 } else {
210 $storage = $this->get_site_storage( $network_level_or_blog_id );
211
212 return $storage->get( $key, $default );
213 }
214 }
215
216 /**
217 * Multisite activated:
218 * true: Save network storage.
219 * int: Save site specific storage.
220 * false|0: Save current site storage.
221 * null: Save network and current site storage.
222 * Site level activated:
223 * Save site storage.
224 *
225 * @author Vova Feldman (@svovaf)
226 * @since 2.0.0
227 *
228 * @param bool|int|null $network_level_or_blog_id
229 */
230 function save( $network_level_or_blog_id = null ) {
231 if ( $this->_is_network_active &&
232 ( true === $network_level_or_blog_id || is_null( $network_level_or_blog_id ) )
233 ) {
234 $this->_network_storage->save();
235 }
236
237 if ( ! $this->_is_network_active || true !== $network_level_or_blog_id ) {
238 $storage = $this->get_site_storage( $network_level_or_blog_id );
239 $storage->save();
240 }
241 }
242
243 /**
244 * @author Vova Feldman (@svovaf)
245 * @since 2.0.0
246 *
247 * @return string
248 */
249 function get_module_slug() {
250 return $this->_module_slug;
251 }
252
253 /**
254 * @author Vova Feldman (@svovaf)
255 * @since 2.0.0
256 *
257 * @return string
258 */
259 function get_module_type() {
260 return $this->_module_type;
261 }
262
263 /**
264 * Migration script to the new storage data structure that is network compatible.
265 *
266 * IMPORTANT:
267 * This method should be executed only after it is determined if this is a network
268 * level compatible product activation.
269 *
270 * @author Vova Feldman (@svovaf)
271 * @since 2.0.0
272 */
273 function migrate_to_network() {
274 if ( ! $this->_is_multisite ) {
275 return;
276 }
277
278 $updated = false;
279
280 if ( ! isset( self::$_NETWORK_OPTIONS_MAP ) ) {
281 self::load_network_options_map();
282 }
283
284 foreach ( self::$_NETWORK_OPTIONS_MAP as $option => $storage_level ) {
285 if ( ! $this->is_multisite_option( $option ) ) {
286 continue;
287 }
288
289 if ( isset( $this->_storage->{$option} ) && ! isset( $this->_network_storage->{$option} ) ) {
290 // Migrate option to the network storage.
291 $this->_network_storage->store( $option, $this->_storage->{$option}, false );
292
293 /**
294 * Remove the option from site level storage.
295 *
296 * IMPORTANT:
297 * The line below is intentionally commented since we want to preserve the option
298 * on the site storage level for "downgrade compatibility". Basically, if the user
299 * will downgrade to an older version of the plugin with the prev storage structure,
300 * it will continue working.
301 *
302 * @todo After a few releases we can remove this.
303 */
304 // $this->_storage->remove($option, false);
305
306 $updated = true;
307 }
308 }
309
310 if ( ! $updated ) {
311 return;
312 }
313
314 // Update network level storage.
315 $this->_network_storage->save();
316 // $this->_storage->save();
317 }
318
319 #--------------------------------------------------------------------------------
320 #region Helper Methods
321 #--------------------------------------------------------------------------------
322
323 /**
324 * We don't want to load the map right away since it's not even needed in a non-MS environment.
325 *
326 * Example:
327 * array(
328 * 'option1' => 0, // Means that the option should always be stored on the network level.
329 * 'option2' => 1, // Means that the option should be stored on the network level only when the module was network level activated.
330 * 'option2' => 2, // Means that the option should be stored on the network level only when the module was network level activated AND the connection was NOT delegated.
331 * 'option3' => 3, // Means that the option should always be stored on the site level.
332 * )
333 *
334 * @author Vova Feldman (@svovaf)
335 * @since 2.0.0
336 */
337 private static function load_network_options_map() {
338 self::$_NETWORK_OPTIONS_MAP = array(
339 // Network level options.
340 'affiliate_application_data' => 0,
341 'connectivity_test' => 0,
342 'has_trial_plan' => 0,
343 'install_sync_timestamp' => 0,
344 'install_sync_cron' => 0,
345 'is_anonymous_ms' => 0,
346 'is_on' => 0,
347 'is_plugin_new_install' => 0,
348 'network_install_blog_id' => 0,
349 'pending_sites_info' => 0,
350 'plugin_last_version' => 0,
351 'plugin_main_file' => 0,
352 'plugin_version' => 0,
353 'sdk_downgrade_mode' => 0,
354 'sdk_last_version' => 0,
355 'sdk_upgrade_mode' => 0,
356 'sdk_version' => 0,
357 'sticky_optin_added_ms' => 0,
358 'subscriptions' => 0,
359 'sync_timestamp' => 0,
360 'sync_cron' => 0,
361 'was_plugin_loaded' => 0,
362 'network_user_id' => 0,
363 'plugin_upgrade_mode' => 0,
364 'plugin_downgrade_mode' => 0,
365 'is_network_connected' => 0,
366 /**
367 * Special flag that is used when a super-admin upgrades to the new version of the SDK that
368 * supports network level integration, when the connection decision wasn't made for all of the
369 * sites in the network.
370 */
371 'is_network_activation' => 0,
372
373 // When network activated, then network level.
374 'install_timestamp' => 1,
375 'prev_is_premium' => 1,
376
377 // If not network activated OR delegated, then site level.
378 'activation_timestamp' => 2,
379 'prev_user_id' => 2,
380 'sticky_optin_added' => 2,
381 'uninstall_reason' => 2,
382 'is_pending_activation' => 2,
383 'pending_license_key' => 2,
384
385 // Site level options.
386 'is_anonymous' => 3,
387 );
388 }
389
390 /**
391 * This method will and should only be executed when is_multisite() is true.
392 *
393 * @author Vova Feldman (@svovaf)
394 * @since 2.0.0
395 *
396 * @param string $key
397 *
398 * @return bool|mixed
399 */
400 private function is_multisite_option( $key ) {
401 if ( ! isset( self::$_NETWORK_OPTIONS_MAP ) ) {
402 self::load_network_options_map();
403 }
404
405 if ( ! isset( self::$_NETWORK_OPTIONS_MAP[ $key ] ) ) {
406 // Option not found -> use site level storage.
407 return false;
408 }
409
410 if ( 0 === self::$_NETWORK_OPTIONS_MAP[ $key ] ) {
411 // Option found and set to always use the network level storage on a multisite.
412 return true;
413 }
414
415 if ( 3 === self::$_NETWORK_OPTIONS_MAP[ $key ] ) {
416 // Option found and set to always use the site level storage on a multisite.
417 return false;
418 }
419
420 if ( ! $this->_is_network_active ) {
421 return false;
422 }
423
424 if ( 1 === self::$_NETWORK_OPTIONS_MAP[ $key ] ) {
425 // Network activated.
426 return true;
427 }
428
429 if ( 2 === self::$_NETWORK_OPTIONS_MAP[ $key ] && ! $this->_is_delegated_connection ) {
430 // Network activated and not delegated.
431 return true;
432 }
433
434 return false;
435 }
436
437 /**
438 * @author Leo Fajardo
439 *
440 * @param string $key
441 * @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_BINARY_MAP).
442 *
443 * @return bool
444 */
445 private function should_use_network_storage( $key, $network_level_or_blog_id = null ) {
446 if ( ! $this->_is_multisite ) {
447 // Not a multisite environment.
448 return false;
449 }
450
451 if ( is_numeric( $network_level_or_blog_id ) ) {
452 // Explicitly asked to use a specified blog storage.
453 return false;
454 }
455
456 if ( is_bool( $network_level_or_blog_id ) ) {
457 // Explicitly specified whether should use the network or blog level storage.
458 return $network_level_or_blog_id;
459 }
460
461 // Determine which storage to use based on the option.
462 return $this->is_multisite_option( $key );
463 }
464
465 /**
466 * @author Vova Feldman (@svovaf)
467 * @since 2.0.0
468 *
469 * @param int $blog_id
470 *
471 * @return \FS_Key_Value_Storage
472 */
473 private function get_site_storage( $blog_id = 0 ) {
474 if ( ! is_numeric( $blog_id ) ||
475 $blog_id == $this->_blog_id ||
476 0 == $blog_id
477 ) {
478 return $this->_storage;
479 }
480
481 return FS_Key_Value_Storage::instance(
482 $this->_module_type . '_data',
483 $this->_storage->get_secondary_id(),
484 $blog_id
485 );
486 }
487
488 #endregion
489
490 #--------------------------------------------------------------------------------
491 #region Magic methods
492 #--------------------------------------------------------------------------------
493
494 function __set( $k, $v ) {
495 if ( $this->should_use_network_storage( $k ) ) {
496 $this->_network_storage->{$k} = $v;
497 } else {
498 $this->_storage->{$k} = $v;
499 }
500 }
501
502 function __isset( $k ) {
503 return $this->should_use_network_storage( $k ) ?
504 isset( $this->_network_storage->{$k} ) :
505 isset( $this->_storage->{$k} );
506 }
507
508 function __unset( $k ) {
509 if ( $this->should_use_network_storage( $k ) ) {
510 unset( $this->_network_storage->{$k} );
511 } else {
512 unset( $this->_storage->{$k} );
513 }
514 }
515
516 function __get( $k ) {
517 return $this->should_use_network_storage( $k ) ?
518 $this->_network_storage->{$k} :
519 $this->_storage->{$k};
520 }
521
522 #endregion
523 }