PageRenderTime 53ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/wp-content/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-options.php

https://gitlab.com/chernushov881/charity-fund
PHP | 481 lines | 192 code | 50 blank | 239 comment | 29 complexity | 0ff76f07a1dce40b625f78eb7e35ef55 MD5 | raw file
  1. <?php
  2. /**
  3. * Options sync module.
  4. *
  5. * @package automattic/jetpack-sync
  6. */
  7. namespace Automattic\Jetpack\Sync\Modules;
  8. use Automattic\Jetpack\Sync\Defaults;
  9. use Automattic\Jetpack\Sync\Settings;
  10. /**
  11. * Class to handle sync for options.
  12. */
  13. class Options extends Module {
  14. /**
  15. * Whitelist for options we want to sync.
  16. *
  17. * @access private
  18. *
  19. * @var array
  20. */
  21. private $options_whitelist;
  22. /**
  23. * Contentless options we want to sync.
  24. *
  25. * @access private
  26. *
  27. * @var array
  28. */
  29. private $options_contentless;
  30. /**
  31. * Sync module name.
  32. *
  33. * @access public
  34. *
  35. * @return string
  36. */
  37. public function name() {
  38. return 'options';
  39. }
  40. /**
  41. * Initialize options action listeners.
  42. *
  43. * @access public
  44. *
  45. * @param callable $callable Action handler callable.
  46. */
  47. public function init_listeners( $callable ) {
  48. // Options.
  49. add_action( 'added_option', $callable, 10, 2 );
  50. add_action( 'updated_option', $callable, 10, 3 );
  51. add_action( 'deleted_option', $callable, 10, 1 );
  52. // Sync Core Icon: Detect changes in Core's Site Icon and make it syncable.
  53. add_action( 'add_option_site_icon', array( $this, 'jetpack_sync_core_icon' ) );
  54. add_action( 'update_option_site_icon', array( $this, 'jetpack_sync_core_icon' ) );
  55. add_action( 'delete_option_site_icon', array( $this, 'jetpack_sync_core_icon' ) );
  56. // Handle deprecated options.
  57. add_filter( 'jetpack_options_whitelist', array( $this, 'add_deprecated_options' ) );
  58. $whitelist_option_handler = array( $this, 'whitelist_options' );
  59. add_filter( 'jetpack_sync_before_enqueue_deleted_option', $whitelist_option_handler );
  60. add_filter( 'jetpack_sync_before_enqueue_added_option', $whitelist_option_handler );
  61. add_filter( 'jetpack_sync_before_enqueue_updated_option', $whitelist_option_handler );
  62. }
  63. /**
  64. * Initialize options action listeners for full sync.
  65. *
  66. * @access public
  67. *
  68. * @param callable $callable Action handler callable.
  69. */
  70. public function init_full_sync_listeners( $callable ) {
  71. add_action( 'jetpack_full_sync_options', $callable );
  72. }
  73. /**
  74. * Initialize the module in the sender.
  75. *
  76. * @access public
  77. */
  78. public function init_before_send() {
  79. // Full sync.
  80. add_filter( 'jetpack_sync_before_send_jetpack_full_sync_options', array( $this, 'expand_options' ) );
  81. }
  82. /**
  83. * Set module defaults.
  84. * Define the options whitelist and contentless options.
  85. *
  86. * @access public
  87. */
  88. public function set_defaults() {
  89. $this->update_options_whitelist();
  90. $this->update_options_contentless();
  91. }
  92. /**
  93. * Set module defaults at a later time.
  94. *
  95. * @access public
  96. */
  97. public function set_late_default() {
  98. /** This filter is already documented in json-endpoints/jetpack/class.wpcom-json-api-get-option-endpoint.php */
  99. $late_options = apply_filters( 'jetpack_options_whitelist', array() );
  100. if ( ! empty( $late_options ) && is_array( $late_options ) ) {
  101. $this->options_whitelist = array_merge( $this->options_whitelist, $late_options );
  102. }
  103. }
  104. /**
  105. * Add old deprecated options to the list of options to keep in sync.
  106. *
  107. * @since 1.14.0
  108. *
  109. * @access public
  110. *
  111. * @param array $options The default list of site options.
  112. */
  113. public function add_deprecated_options( $options ) {
  114. global $wp_version;
  115. $deprecated_options = array(
  116. 'blacklist_keys' => '5.5-alpha', // Replaced by disallowed_keys.
  117. 'comment_whitelist' => '5.5-alpha', // Replaced by comment_previously_approved.
  118. );
  119. foreach ( $deprecated_options as $option => $version ) {
  120. if ( version_compare( $wp_version, $version, '<=' ) ) {
  121. $options[] = $option;
  122. }
  123. }
  124. return $options;
  125. }
  126. /**
  127. * Enqueue the options actions for full sync.
  128. *
  129. * @access public
  130. *
  131. * @param array $config Full sync configuration for this sync module.
  132. * @param int $max_items_to_enqueue Maximum number of items to enqueue.
  133. * @param boolean $state True if full sync has finished enqueueing this module, false otherwise.
  134. * @return array Number of actions enqueued, and next module state.
  135. */
  136. public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
  137. /**
  138. * Tells the client to sync all options to the server
  139. *
  140. * @since 1.6.3
  141. * @since-jetpack 4.2.0
  142. *
  143. * @param boolean Whether to expand options (should always be true)
  144. */
  145. do_action( 'jetpack_full_sync_options', true );
  146. // The number of actions enqueued, and next module state (true == done).
  147. return array( 1, true );
  148. }
  149. /**
  150. * Send the options actions for full sync.
  151. *
  152. * @access public
  153. *
  154. * @param array $config Full sync configuration for this sync module.
  155. * @param int $send_until The timestamp until the current request can send.
  156. * @param array $state This module Full Sync status.
  157. *
  158. * @return array This module Full Sync status.
  159. */
  160. public function send_full_sync_actions( $config, $send_until, $state ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
  161. // we call this instead of do_action when sending immediately.
  162. $this->send_action( 'jetpack_full_sync_options', array( true ) );
  163. // The number of actions enqueued, and next module state (true == done).
  164. return array( 'finished' => true );
  165. }
  166. /**
  167. * Retrieve an estimated number of actions that will be enqueued.
  168. *
  169. * @access public
  170. *
  171. * @param array $config Full sync configuration for this sync module.
  172. * @return int Number of items yet to be enqueued.
  173. */
  174. public function estimate_full_sync_actions( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
  175. return 1;
  176. }
  177. /**
  178. * Retrieve the actions that will be sent for this module during a full sync.
  179. *
  180. * @access public
  181. *
  182. * @return array Full sync actions of this module.
  183. */
  184. public function get_full_sync_actions() {
  185. return array( 'jetpack_full_sync_options' );
  186. }
  187. /**
  188. * Retrieve all options as per the current options whitelist.
  189. * Public so that we don't have to store so much data all the options twice.
  190. *
  191. * @access public
  192. *
  193. * @return array All options.
  194. */
  195. public function get_all_options() {
  196. $options = array();
  197. $random_string = wp_generate_password();
  198. foreach ( $this->options_whitelist as $option ) {
  199. if ( 0 === strpos( $option, Settings::SETTINGS_OPTION_PREFIX ) ) {
  200. $option_value = Settings::get_setting( str_replace( Settings::SETTINGS_OPTION_PREFIX, '', $option ) );
  201. $options[ $option ] = $option_value;
  202. } else {
  203. $option_value = get_option( $option, $random_string );
  204. if ( $option_value !== $random_string ) {
  205. $options[ $option ] = $option_value;
  206. }
  207. }
  208. }
  209. // Add theme mods.
  210. $theme_mods_option = 'theme_mods_' . get_option( 'stylesheet' );
  211. $theme_mods_value = get_option( $theme_mods_option, $random_string );
  212. if ( $theme_mods_value === $random_string ) {
  213. return $options;
  214. }
  215. $this->filter_theme_mods( $theme_mods_value );
  216. $options[ $theme_mods_option ] = $theme_mods_value;
  217. return $options;
  218. }
  219. /**
  220. * Update the options whitelist to the default one.
  221. *
  222. * @access public
  223. */
  224. public function update_options_whitelist() {
  225. $this->options_whitelist = Defaults::get_options_whitelist();
  226. }
  227. /**
  228. * Set the options whitelist.
  229. *
  230. * @access public
  231. *
  232. * @param array $options The new options whitelist.
  233. */
  234. public function set_options_whitelist( $options ) {
  235. $this->options_whitelist = $options;
  236. }
  237. /**
  238. * Get the options whitelist.
  239. *
  240. * @access public
  241. *
  242. * @return array The options whitelist.
  243. */
  244. public function get_options_whitelist() {
  245. return $this->options_whitelist;
  246. }
  247. /**
  248. * Update the contentless options to the defaults.
  249. *
  250. * @access public
  251. */
  252. public function update_options_contentless() {
  253. $this->options_contentless = Defaults::get_options_contentless();
  254. }
  255. /**
  256. * Get the contentless options.
  257. *
  258. * @access public
  259. *
  260. * @return array Array of the contentless options.
  261. */
  262. public function get_options_contentless() {
  263. return $this->options_contentless;
  264. }
  265. /**
  266. * Reject any options that aren't whitelisted or contentless.
  267. *
  268. * @access public
  269. *
  270. * @param array $args The hook parameters.
  271. * @return array $args The hook parameters.
  272. */
  273. public function whitelist_options( $args ) {
  274. // Reject non-whitelisted options.
  275. if ( ! $this->is_whitelisted_option( $args[0] ) ) {
  276. return false;
  277. }
  278. // Filter our weird array( false ) value for theme_mods_*.
  279. if ( 'theme_mods_' === substr( $args[0], 0, 11 ) ) {
  280. $this->filter_theme_mods( $args[1] );
  281. if ( isset( $args[2] ) ) {
  282. $this->filter_theme_mods( $args[2] );
  283. }
  284. }
  285. // Set value(s) of contentless option to empty string(s).
  286. if ( $this->is_contentless_option( $args[0] ) ) {
  287. // Create a new array matching length of $args, containing empty strings.
  288. $empty = array_fill( 0, count( $args ), '' );
  289. $empty[0] = $args[0];
  290. return $empty;
  291. }
  292. return $args;
  293. }
  294. /**
  295. * Whether a certain option is whitelisted for sync.
  296. *
  297. * @access public
  298. *
  299. * @param string $option Option name.
  300. * @return boolean Whether the option is whitelisted.
  301. */
  302. public function is_whitelisted_option( $option ) {
  303. return in_array( $option, $this->options_whitelist, true ) || 'theme_mods_' === substr( $option, 0, 11 );
  304. }
  305. /**
  306. * Whether a certain option is a contentless one.
  307. *
  308. * @access private
  309. *
  310. * @param string $option Option name.
  311. * @return boolean Whether the option is contentless.
  312. */
  313. private function is_contentless_option( $option ) {
  314. return in_array( $option, $this->options_contentless, true );
  315. }
  316. /**
  317. * Filters out falsy values from theme mod options.
  318. *
  319. * @access private
  320. *
  321. * @param array $value Option value.
  322. */
  323. private function filter_theme_mods( &$value ) {
  324. if ( is_array( $value ) && isset( $value[0] ) ) {
  325. unset( $value[0] );
  326. }
  327. }
  328. /**
  329. * Handle changes in the core site icon and sync them.
  330. *
  331. * @access public
  332. */
  333. public function jetpack_sync_core_icon() {
  334. $url = get_site_icon_url();
  335. $jetpack_url = \Jetpack_Options::get_option( 'site_icon_url' );
  336. if ( defined( 'JETPACK__PLUGIN_DIR' ) ) {
  337. if ( ! function_exists( 'jetpack_site_icon_url' ) ) {
  338. require_once JETPACK__PLUGIN_DIR . 'modules/site-icon/site-icon-functions.php';
  339. }
  340. $jetpack_url = jetpack_site_icon_url();
  341. }
  342. // If there's a core icon, maybe update the option. If not, fall back to Jetpack's.
  343. if ( ! empty( $url ) && $jetpack_url !== $url ) {
  344. // This is the option that is synced with dotcom.
  345. \Jetpack_Options::update_option( 'site_icon_url', $url );
  346. } elseif ( empty( $url ) ) {
  347. \Jetpack_Options::delete_option( 'site_icon_url' );
  348. }
  349. }
  350. /**
  351. * Expand all options within a hook before they are serialized and sent to the server.
  352. *
  353. * @access public
  354. *
  355. * @param array $args The hook parameters.
  356. * @return array $args The hook parameters.
  357. */
  358. public function expand_options( $args ) {
  359. if ( $args[0] ) {
  360. return $this->get_all_options();
  361. }
  362. return $args;
  363. }
  364. /**
  365. * Return Total number of objects.
  366. *
  367. * @param array $config Full Sync config.
  368. *
  369. * @return int total
  370. */
  371. public function total( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
  372. return count( Defaults::get_options_whitelist() );
  373. }
  374. /**
  375. * Retrieve a set of options by their IDs.
  376. *
  377. * @access public
  378. *
  379. * @param string $object_type Object type.
  380. * @param array $ids Object IDs.
  381. * @return array Array of objects.
  382. */
  383. public function get_objects_by_id( $object_type, $ids ) {
  384. if ( empty( $ids ) || empty( $object_type ) || 'option' !== $object_type ) {
  385. return array();
  386. }
  387. $objects = array();
  388. foreach ( (array) $ids as $id ) {
  389. $object = $this->get_object_by_id( $object_type, $id );
  390. // Only add object if we have the object.
  391. if ( 'OPTION-DOES-NOT-EXIST' !== $object ) {
  392. if ( 'all' === $id ) {
  393. // If all was requested it contains all options and can simply be returned.
  394. return $object;
  395. }
  396. $objects[ $id ] = $object;
  397. }
  398. }
  399. return $objects;
  400. }
  401. /**
  402. * Retrieve an option by its name.
  403. *
  404. * @access public
  405. *
  406. * @param string $object_type Type of the sync object.
  407. * @param string $id ID of the sync object.
  408. * @return mixed Value of Option or 'OPTION-DOES-NOT-EXIST' if not found.
  409. */
  410. public function get_object_by_id( $object_type, $id ) {
  411. if ( 'option' === $object_type ) {
  412. // Utilize Random string as default value to distinguish between false and not exist.
  413. $random_string = wp_generate_password();
  414. // Only whitelisted options can be returned.
  415. if ( in_array( $id, $this->options_whitelist, true ) ) {
  416. if ( 0 === strpos( $id, Settings::SETTINGS_OPTION_PREFIX ) ) {
  417. $option_value = Settings::get_setting( str_replace( Settings::SETTINGS_OPTION_PREFIX, '', $id ) );
  418. return $option_value;
  419. } else {
  420. $option_value = get_option( $id, $random_string );
  421. if ( $option_value !== $random_string ) {
  422. return $option_value;
  423. }
  424. }
  425. } elseif ( 'all' === $id ) {
  426. return $this->get_all_options();
  427. }
  428. }
  429. return 'OPTION-DOES-NOT-EXIST';
  430. }
  431. }