PluginProbe
BlockSpare – Gutenberg Blocks for News, Magazine, Blog & Business Websites / trunk
BlockSpare – Gutenberg Blocks for News, Magazine, Blog & Business Websites vtrunk
4.1.0 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.1.0 1.1.1 1.1.2 1.2.0 1.2.1 1.2.2 1.2.3 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 2.1.2 2.5.0 2.5.3 2.6.1 All 38 releases
← All changes | freemius/includes/class-fs-options.php +430 -430 2.1.2trunk View file →
@@ -1,431 +1,431 @@
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_Options
15 - *
16 - * A wrapper class for handling network level and single site level options.
17 - */
18 - class FS_Options {
19 - /**
20 - * @var string
21 - */
22 - private $_id;
23 -
24 - /**
25 - * @var array[string]FS_Options {
26 - * @key string
27 - * @value FS_Options
28 - * }
29 - */
30 - private static $_instances;
31 -
32 - /**
33 - * @var FS_Option_Manager Site level options.
34 - */
35 - private $_options;
36 -
37 - /**
38 - * @var FS_Option_Manager Network level options.
39 - */
40 - private $_network_options;
41 -
42 - /**
43 - * @var int The ID of the blog that is associated with the current site level options.
44 - */
45 - private $_blog_id = 0;
46 -
47 - /**
48 - * @var bool
49 - */
50 - private $_is_multisite;
51 -
52 - /**
53 - * @var string[] Lazy collection of params on the site level.
54 - */
55 - private static $_SITE_OPTIONS_MAP;
56 -
57 - /**
58 - * @author Leo Fajardo (@leorw)
59 - * @since 2.0.0
60 - *
61 - * @param string $id
62 - * @param bool $load
63 - *
64 - * @return FS_Options
65 - */
66 - static function instance( $id, $load = false ) {
67 - if ( ! isset( self::$_instances[ $id ] ) ) {
68 - self::$_instances[ $id ] = new FS_Options( $id, $load );
69 - }
70 -
71 - return self::$_instances[ $id ];
72 - }
73 -
74 - /**
75 - * @author Leo Fajardo (@leorw)
76 - * @since 2.0.0
77 - *
78 - * @param string $id
79 - * @param bool $load
80 - */
81 - private function __construct( $id, $load = false ) {
82 - $this->_id = $id;
83 - $this->_is_multisite = is_multisite();
84 -
85 - if ( $this->_is_multisite ) {
86 - $this->_blog_id = get_current_blog_id();
87 - $this->_network_options = FS_Option_Manager::get_manager( $id, $load, true );
88 - }
89 -
90 - $this->_options = FS_Option_Manager::get_manager( $id, $load, $this->_blog_id );
91 - }
92 -
93 - /**
94 - * Switch the context of the site level options manager.
95 - *
96 - * @author Vova Feldman (@svovaf)
97 - * @since 2.0.0
98 - *
99 - * @param $blog_id
100 - */
101 - function set_site_blog_context( $blog_id ) {
102 - $this->_blog_id = $blog_id;
103 -
104 - $this->_options = FS_Option_Manager::get_manager( $this->_id, false, $this->_blog_id );
105 - }
106 -
107 - /**
108 - * @author Leo Fajardo (@leorw)
109 - *
110 - * @param string $option
111 - * @param mixed $default
112 - * @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::$_SITE_LEVEL_PARAMS).
113 - *
114 - * @return mixed
115 - */
116 - function get_option( $option, $default = null, $network_level_or_blog_id = null ) {
117 - if ( $this->should_use_network_storage( $option, $network_level_or_blog_id ) ) {
118 - return $this->_network_options->get_option( $option, $default );
119 - }
120 -
121 - $site_options = $this->get_site_options( $network_level_or_blog_id );
122 -
123 - return $site_options->get_option( $option, $default );
124 - }
125 -
126 - /**
127 - * @author Leo Fajardo (@leorw)
128 - * @since 2.0.0
129 - *
130 - * @param string $option
131 - * @param mixed $value
132 - * @param bool $flush
133 - * @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::$_SITE_LEVEL_PARAMS).
134 - */
135 - function set_option( $option, $value, $flush = false, $network_level_or_blog_id = null ) {
136 - if ( $this->should_use_network_storage( $option, $network_level_or_blog_id ) ) {
137 - $this->_network_options->set_option( $option, $value, $flush );
138 - } else {
139 - $site_options = $this->get_site_options( $network_level_or_blog_id );
140 - $site_options->set_option( $option, $value, $flush );
141 - }
142 - }
143 -
144 - /**
145 - * @author Vova Feldman (@svovaf)
146 - * @since 2.0.0
147 - *
148 - * @param string $option
149 - * @param bool $flush
150 - * @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::$_SITE_LEVEL_PARAMS).
151 - */
152 - function unset_option( $option, $flush = false, $network_level_or_blog_id = null ) {
153 - if ( $this->should_use_network_storage( $option, $network_level_or_blog_id ) ) {
154 - $this->_network_options->unset_option( $option, $flush );
155 - } else {
156 - $site_options = $this->get_site_options( $network_level_or_blog_id );
157 - $site_options->unset_option( $option, $flush );
158 - }
159 - }
160 -
161 - /**
162 - * @author Leo Fajardo (@leorw)
163 - * @since 2.0.0
164 - *
165 - * @param bool $flush
166 - * @param bool $network_level
167 - */
168 - function load( $flush = false, $network_level = true ) {
169 - if ( $this->_is_multisite && $network_level ) {
170 - $this->_network_options->load( $flush );
171 - } else {
172 - $this->_options->load( $flush );
173 - }
174 - }
175 -
176 - /**
177 - * @author Leo Fajardo (@leorw)
178 - * @since 2.0.0
179 - *
180 - * @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`, store both network storage and the current context blog storage.
181 - */
182 - function store( $network_level_or_blog_id = null ) {
183 - if ( ! $this->_is_multisite ||
184 - false === $network_level_or_blog_id ||
185 - 0 == $network_level_or_blog_id ||
186 - is_null( $network_level_or_blog_id )
187 - ) {
188 - $site_options = $this->get_site_options( $network_level_or_blog_id );
189 - $site_options->store();
190 - }
191 -
192 - if ( $this->_is_multisite &&
193 - ( is_null( $network_level_or_blog_id ) || true === $network_level_or_blog_id )
194 - ) {
195 - $this->_network_options->store();
196 - }
197 - }
198 -
199 - /**
200 - * @author Vova Feldman (@svovaf)
201 - * @since 2.0.0
202 - *
203 - * @param int|null|bool $network_level_or_blog_id
204 - * @param bool $flush
205 - */
206 - function clear( $network_level_or_blog_id = null, $flush = false ) {
207 - if ( ! $this->_is_multisite ||
208 - false === $network_level_or_blog_id ||
209 - is_null( $network_level_or_blog_id ) ||
210 - is_numeric( $network_level_or_blog_id )
211 - ) {
212 - $site_options = $this->get_site_options( $network_level_or_blog_id );
213 - $site_options->clear( $flush );
214 - }
215 -
216 - if ( $this->_is_multisite &&
217 - ( true === $network_level_or_blog_id || is_null( $network_level_or_blog_id ) )
218 - ) {
219 - $this->_network_options->clear( $flush );
220 - }
221 - }
222 -
223 - /**
224 - * Migration script to the new storage data structure that is network compatible.
225 - *
226 - * IMPORTANT:
227 - * This method should be executed only after it is determined if this is a network
228 - * level compatible product activation.
229 - *
230 - * @author Vova Feldman (@svovaf)
231 - * @since 2.0.0
232 - *
233 - * @param int $blog_id
234 - */
235 - function migrate_to_network( $blog_id = 0 ) {
236 - if ( ! $this->_is_multisite ) {
237 - return;
238 - }
239 -
240 - $updated = false;
241 -
242 - $site_options = $this->get_site_options( $blog_id );
243 -
244 - $keys = $site_options->get_options_keys();
245 -
246 - foreach ( $keys as $option ) {
247 - if ( $this->is_site_option( $option ) ||
248 - // Don't move admin notices to the network storage.
249 - in_array($option, array(
250 - // Don't move admin notices to the network storage.
251 - 'admin_notices',
252 - // Don't migrate the module specific data, it will be migrated by the FS_Storage.
253 - 'plugin_data',
254 - 'theme_data',
255 - ))
256 - ) {
257 - continue;
258 - }
259 -
260 - $option_updated = false;
261 -
262 - // Migrate option to the network storage.
263 - $site_option = $site_options->get_option( $option );
264 -
265 - if ( ! $this->_network_options->has_option( $option ) ) {
266 - // Option not set on the network level, so just set it.
267 - $this->_network_options->set_option( $option, $site_option, false );
268 -
269 - $option_updated = true;
270 - } else {
271 - // Option already set on the network level, so we need to merge it inelegantly.
272 - $network_option = $this->_network_options->get_option( $option );
273 -
274 - if ( is_array( $network_option ) && is_array( $site_option ) ) {
275 - // Option is an array.
276 - foreach ( $site_option as $key => $value ) {
277 - if ( ! isset( $network_option[ $key ] ) ) {
278 - $network_option[ $key ] = $value;
279 -
280 - $option_updated = true;
281 - } else if ( is_array( $network_option[ $key ] ) && is_array( $value ) ) {
282 - if ( empty( $network_option[ $key ] ) ) {
283 - $network_option[ $key ] = $value;
284 -
285 - $option_updated = true;
286 - } else if ( empty( $value ) ) {
287 - // Do nothing.
288 - } else {
289 - reset($value);
290 - $first_key = key($value);
291 - if ( $value[$first_key] instanceof FS_Entity ) {
292 - // Merge entities by IDs.
293 - $network_entities_ids = array();
294 - foreach ( $network_option[ $key ] as $entity ) {
295 - $network_entities_ids[ $entity->id ] = true;
296 - }
297 -
298 - foreach ( $value as $entity ) {
299 - if ( ! isset( $network_entities_ids[ $entity->id ] ) ) {
300 - $network_option[ $key ][] = $entity;
301 -
302 - $option_updated = true;
303 - }
304 - }
305 - }
306 - }
307 - }
308 - }
309 - }
310 -
311 - if ( $option_updated ) {
312 - $this->_network_options->set_option( $option, $network_option, false );
313 - }
314 - }
315 -
316 - /**
317 - * Remove the option from site level storage.
318 - *
319 - * IMPORTANT:
320 - * The line below is intentionally commented since we want to preserve the option
321 - * on the site storage level for "downgrade compatibility". Basically, if the user
322 - * will downgrade to an older version of the plugin with the prev storage structure,
323 - * it will continue working.
324 - *
325 - * @todo After a few releases we can remove this.
326 - */
327 -// $site_options->unset_option($option, false);
328 -
329 - if ( $option_updated ) {
330 - $updated = true;
331 - }
332 - }
333 -
334 - if ( ! $updated ) {
335 - return;
336 - }
337 -
338 - // Update network level storage.
339 - $this->_network_options->store();
340 -// $site_options->store();
341 - }
342 -
343 -
344 - #--------------------------------------------------------------------------------
345 - #region Helper Methods
346 - #--------------------------------------------------------------------------------
347 -
348 - /**
349 - * We don't want to load the map right away since it's not even needed in a non-MS environment.
350 - *
351 - * @author Vova Feldman (@svovaf)
352 - * @since 2.0.0
353 - */
354 - private static function load_site_options_map() {
355 - self::$_SITE_OPTIONS_MAP = array(
356 - 'sites' => true,
357 - 'theme_sites' => true,
358 - 'unique_id' => true,
359 - 'active_plugins' => true,
360 - );
361 - }
362 -
363 - /**
364 - * @author Vova Feldman (@svovaf)
365 - * @since 2.0.0
366 - *
367 - * @param string $option
368 - *
369 - * @return bool
370 - */
371 - private function is_site_option( $option ) {
372 - if ( WP_FS__ACCOUNTS_OPTION_NAME != $this->_id ) {
373 - return false;
374 - }
375 -
376 - if ( ! isset( self::$_SITE_OPTIONS_MAP ) ) {
377 - self::load_site_options_map();
378 - }
379 -
380 - return isset( self::$_SITE_OPTIONS_MAP[ $option ] );
381 - }
382 -
383 - /**
384 - * @author Vova Feldman (@svovaf)
385 - * @since 2.0.0
386 - *
387 - * @param int $blog_id
388 - *
389 - * @return FS_Option_Manager
390 - */
391 - private function get_site_options( $blog_id = 0 ) {
392 - if ( 0 == $blog_id || $blog_id == $this->_blog_id ) {
393 - return $this->_options;
394 - }
395 -
396 - return FS_Option_Manager::get_manager( $this->_id, true, $blog_id );
397 - }
398 -
399 - /**
400 - * Check if an option should be stored on the MS network storage.
401 - *
402 - * @author Vova Feldman (@svovaf)
403 - * @since 2.0.0
404 - *
405 - * @param string $option
406 - * @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::$_SITE_LEVEL_PARAMS).
407 - *
408 - * @return bool
409 - */
410 - private function should_use_network_storage( $option, $network_level_or_blog_id = null ) {
411 - if ( ! $this->_is_multisite ) {
412 - // Not a multisite environment.
413 - return false;
414 - }
415 -
416 - if ( is_numeric( $network_level_or_blog_id ) ) {
417 - // Explicitly asked to use a specified blog storage.
418 - return false;
419 - }
420 -
421 - if ( is_bool( $network_level_or_blog_id ) ) {
422 - // Explicitly specified whether should use the network or blog level storage.
423 - return $network_level_or_blog_id;
424 - }
425 -
426 - // Determine which storage to use based on the option.
427 - return ! $this->is_site_option( $option );
428 - }
429 -
430 - #endregion
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_Options
15 + *
16 + * A wrapper class for handling network level and single site level options.
17 + */
18 + class FS_Options {
19 + /**
20 + * @var string
21 + */
22 + private $_id;
23 +
24 + /**
25 + * @var array[string]FS_Options {
26 + * @key string
27 + * @value FS_Options
28 + * }
29 + */
30 + private static $_instances;
31 +
32 + /**
33 + * @var FS_Option_Manager Site level options.
34 + */
35 + private $_options;
36 +
37 + /**
38 + * @var FS_Option_Manager Network level options.
39 + */
40 + private $_network_options;
41 +
42 + /**
43 + * @var int The ID of the blog that is associated with the current site level options.
44 + */
45 + private $_blog_id = 0;
46 +
47 + /**
48 + * @var bool
49 + */
50 + private $_is_multisite;
51 +
52 + /**
53 + * @var string[] Lazy collection of params on the site level.
54 + */
55 + private static $_SITE_OPTIONS_MAP;
56 +
57 + /**
58 + * @author Leo Fajardo (@leorw)
59 + * @since 2.0.0
60 + *
61 + * @param string $id
62 + * @param bool $load
63 + *
64 + * @return FS_Options
65 + */
66 + static function instance( $id, $load = false ) {
67 + if ( ! isset( self::$_instances[ $id ] ) ) {
68 + self::$_instances[ $id ] = new FS_Options( $id, $load );
69 + }
70 +
71 + return self::$_instances[ $id ];
72 + }
73 +
74 + /**
75 + * @author Leo Fajardo (@leorw)
76 + * @since 2.0.0
77 + *
78 + * @param string $id
79 + * @param bool $load
80 + */
81 + private function __construct( $id, $load = false ) {
82 + $this->_id = $id;
83 + $this->_is_multisite = is_multisite();
84 +
85 + if ( $this->_is_multisite ) {
86 + $this->_blog_id = get_current_blog_id();
87 + $this->_network_options = FS_Option_Manager::get_manager( $id, $load, true );
88 + }
89 +
90 + $this->_options = FS_Option_Manager::get_manager( $id, $load, $this->_blog_id );
91 + }
92 +
93 + /**
94 + * Switch the context of the site level options manager.
95 + *
96 + * @author Vova Feldman (@svovaf)
97 + * @since 2.0.0
98 + *
99 + * @param $blog_id
100 + */
101 + function set_site_blog_context( $blog_id ) {
102 + $this->_blog_id = $blog_id;
103 +
104 + $this->_options = FS_Option_Manager::get_manager( $this->_id, false, $this->_blog_id );
105 + }
106 +
107 + /**
108 + * @author Leo Fajardo (@leorw)
109 + *
110 + * @param string $option
111 + * @param mixed $default
112 + * @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::$_SITE_LEVEL_PARAMS).
113 + *
114 + * @return mixed
115 + */
116 + function get_option( $option, $default = null, $network_level_or_blog_id = null ) {
117 + if ( $this->should_use_network_storage( $option, $network_level_or_blog_id ) ) {
118 + return $this->_network_options->get_option( $option, $default );
119 + }
120 +
121 + $site_options = $this->get_site_options( $network_level_or_blog_id );
122 +
123 + return $site_options->get_option( $option, $default );
124 + }
125 +
126 + /**
127 + * @author Leo Fajardo (@leorw)
128 + * @since 2.0.0
129 + *
130 + * @param string $option
131 + * @param mixed $value
132 + * @param bool $flush
133 + * @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::$_SITE_LEVEL_PARAMS).
134 + */
135 + function set_option( $option, $value, $flush = false, $network_level_or_blog_id = null ) {
136 + if ( $this->should_use_network_storage( $option, $network_level_or_blog_id ) ) {
137 + $this->_network_options->set_option( $option, $value, $flush );
138 + } else {
139 + $site_options = $this->get_site_options( $network_level_or_blog_id );
140 + $site_options->set_option( $option, $value, $flush );
141 + }
142 + }
143 +
144 + /**
145 + * @author Vova Feldman (@svovaf)
146 + * @since 2.0.0
147 + *
148 + * @param string $option
149 + * @param bool $flush
150 + * @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::$_SITE_LEVEL_PARAMS).
151 + */
152 + function unset_option( $option, $flush = false, $network_level_or_blog_id = null ) {
153 + if ( $this->should_use_network_storage( $option, $network_level_or_blog_id ) ) {
154 + $this->_network_options->unset_option( $option, $flush );
155 + } else {
156 + $site_options = $this->get_site_options( $network_level_or_blog_id );
157 + $site_options->unset_option( $option, $flush );
158 + }
159 + }
160 +
161 + /**
162 + * @author Leo Fajardo (@leorw)
163 + * @since 2.0.0
164 + *
165 + * @param bool $flush
166 + * @param bool $network_level
167 + */
168 + function load( $flush = false, $network_level = true ) {
169 + if ( $this->_is_multisite && $network_level ) {
170 + $this->_network_options->load( $flush );
171 + } else {
172 + $this->_options->load( $flush );
173 + }
174 + }
175 +
176 + /**
177 + * @author Leo Fajardo (@leorw)
178 + * @since 2.0.0
179 + *
180 + * @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`, store both network storage and the current context blog storage.
181 + */
182 + function store( $network_level_or_blog_id = null ) {
183 + if ( ! $this->_is_multisite ||
184 + false === $network_level_or_blog_id ||
185 + 0 == $network_level_or_blog_id ||
186 + is_null( $network_level_or_blog_id )
187 + ) {
188 + $site_options = $this->get_site_options( $network_level_or_blog_id );
189 + $site_options->store();
190 + }
191 +
192 + if ( $this->_is_multisite &&
193 + ( is_null( $network_level_or_blog_id ) || true === $network_level_or_blog_id )
194 + ) {
195 + $this->_network_options->store();
196 + }
197 + }
198 +
199 + /**
200 + * @author Vova Feldman (@svovaf)
201 + * @since 2.0.0
202 + *
203 + * @param int|null|bool $network_level_or_blog_id
204 + * @param bool $flush
205 + */
206 + function clear( $network_level_or_blog_id = null, $flush = false ) {
207 + if ( ! $this->_is_multisite ||
208 + false === $network_level_or_blog_id ||
209 + is_null( $network_level_or_blog_id ) ||
210 + is_numeric( $network_level_or_blog_id )
211 + ) {
212 + $site_options = $this->get_site_options( $network_level_or_blog_id );
213 + $site_options->clear( $flush );
214 + }
215 +
216 + if ( $this->_is_multisite &&
217 + ( true === $network_level_or_blog_id || is_null( $network_level_or_blog_id ) )
218 + ) {
219 + $this->_network_options->clear( $flush );
220 + }
221 + }
222 +
223 + /**
224 + * Migration script to the new storage data structure that is network compatible.
225 + *
226 + * IMPORTANT:
227 + * This method should be executed only after it is determined if this is a network
228 + * level compatible product activation.
229 + *
230 + * @author Vova Feldman (@svovaf)
231 + * @since 2.0.0
232 + *
233 + * @param int $blog_id
234 + */
235 + function migrate_to_network( $blog_id = 0 ) {
236 + if ( ! $this->_is_multisite ) {
237 + return;
238 + }
239 +
240 + $updated = false;
241 +
242 + $site_options = $this->get_site_options( $blog_id );
243 +
244 + $keys = $site_options->get_options_keys();
245 +
246 + foreach ( $keys as $option ) {
247 + if ( $this->is_site_option( $option ) ||
248 + // Don't move admin notices to the network storage.
249 + in_array($option, array(
250 + // Don't move admin notices to the network storage.
251 + 'admin_notices',
252 + // Don't migrate the module specific data, it will be migrated by the FS_Storage.
253 + 'plugin_data',
254 + 'theme_data',
255 + ))
256 + ) {
257 + continue;
258 + }
259 +
260 + $option_updated = false;
261 +
262 + // Migrate option to the network storage.
263 + $site_option = $site_options->get_option( $option );
264 +
265 + if ( ! $this->_network_options->has_option( $option ) ) {
266 + // Option not set on the network level, so just set it.
267 + $this->_network_options->set_option( $option, $site_option, false );
268 +
269 + $option_updated = true;
270 + } else {
271 + // Option already set on the network level, so we need to merge it inelegantly.
272 + $network_option = $this->_network_options->get_option( $option );
273 +
274 + if ( is_array( $network_option ) && is_array( $site_option ) ) {
275 + // Option is an array.
276 + foreach ( $site_option as $key => $value ) {
277 + if ( ! isset( $network_option[ $key ] ) ) {
278 + $network_option[ $key ] = $value;
279 +
280 + $option_updated = true;
281 + } else if ( is_array( $network_option[ $key ] ) && is_array( $value ) ) {
282 + if ( empty( $network_option[ $key ] ) ) {
283 + $network_option[ $key ] = $value;
284 +
285 + $option_updated = true;
286 + } else if ( empty( $value ) ) {
287 + // Do nothing.
288 + } else {
289 + reset($value);
290 + $first_key = key($value);
291 + if ( $value[$first_key] instanceof FS_Entity ) {
292 + // Merge entities by IDs.
293 + $network_entities_ids = array();
294 + foreach ( $network_option[ $key ] as $entity ) {
295 + $network_entities_ids[ $entity->id ] = true;
296 + }
297 +
298 + foreach ( $value as $entity ) {
299 + if ( ! isset( $network_entities_ids[ $entity->id ] ) ) {
300 + $network_option[ $key ][] = $entity;
301 +
302 + $option_updated = true;
303 + }
304 + }
305 + }
306 + }
307 + }
308 + }
309 + }
310 +
311 + if ( $option_updated ) {
312 + $this->_network_options->set_option( $option, $network_option, false );
313 + }
314 + }
315 +
316 + /**
317 + * Remove the option from site level storage.
318 + *
319 + * IMPORTANT:
320 + * The line below is intentionally commented since we want to preserve the option
321 + * on the site storage level for "downgrade compatibility". Basically, if the user
322 + * will downgrade to an older version of the plugin with the prev storage structure,
323 + * it will continue working.
324 + *
325 + * @todo After a few releases we can remove this.
326 + */
327 +// $site_options->unset_option($option, false);
328 +
329 + if ( $option_updated ) {
330 + $updated = true;
331 + }
332 + }
333 +
334 + if ( ! $updated ) {
335 + return;
336 + }
337 +
338 + // Update network level storage.
339 + $this->_network_options->store();
340 +// $site_options->store();
341 + }
342 +
343 +
344 + #--------------------------------------------------------------------------------
345 + #region Helper Methods
346 + #--------------------------------------------------------------------------------
347 +
348 + /**
349 + * We don't want to load the map right away since it's not even needed in a non-MS environment.
350 + *
351 + * @author Vova Feldman (@svovaf)
352 + * @since 2.0.0
353 + */
354 + private static function load_site_options_map() {
355 + self::$_SITE_OPTIONS_MAP = array(
356 + 'sites' => true,
357 + 'theme_sites' => true,
358 + 'unique_id' => true,
359 + 'active_plugins' => true,
360 + );
361 + }
362 +
363 + /**
364 + * @author Vova Feldman (@svovaf)
365 + * @since 2.0.0
366 + *
367 + * @param string $option
368 + *
369 + * @return bool
370 + */
371 + private function is_site_option( $option ) {
372 + if ( WP_FS__ACCOUNTS_OPTION_NAME != $this->_id ) {
373 + return false;
374 + }
375 +
376 + if ( ! isset( self::$_SITE_OPTIONS_MAP ) ) {
377 + self::load_site_options_map();
378 + }
379 +
380 + return isset( self::$_SITE_OPTIONS_MAP[ $option ] );
381 + }
382 +
383 + /**
384 + * @author Vova Feldman (@svovaf)
385 + * @since 2.0.0
386 + *
387 + * @param int $blog_id
388 + *
389 + * @return FS_Option_Manager
390 + */
391 + private function get_site_options( $blog_id = 0 ) {
392 + if ( 0 == $blog_id || $blog_id == $this->_blog_id ) {
393 + return $this->_options;
394 + }
395 +
396 + return FS_Option_Manager::get_manager( $this->_id, true, $blog_id );
397 + }
398 +
399 + /**
400 + * Check if an option should be stored on the MS network storage.
401 + *
402 + * @author Vova Feldman (@svovaf)
403 + * @since 2.0.0
404 + *
405 + * @param string $option
406 + * @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::$_SITE_LEVEL_PARAMS).
407 + *
408 + * @return bool
409 + */
410 + private function should_use_network_storage( $option, $network_level_or_blog_id = null ) {
411 + if ( ! $this->_is_multisite ) {
412 + // Not a multisite environment.
413 + return false;
414 + }
415 +
416 + if ( is_numeric( $network_level_or_blog_id ) ) {
417 + // Explicitly asked to use a specified blog storage.
418 + return false;
419 + }
420 +
421 + if ( is_bool( $network_level_or_blog_id ) ) {
422 + // Explicitly specified whether should use the network or blog level storage.
423 + return $network_level_or_blog_id;
424 + }
425 +
426 + // Determine which storage to use based on the option.
427 + return ! $this->is_site_option( $option );
428 + }
429 +
430 + #endregion
431 431 }