PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 6.4
Jetpack – WP Security, Backup, Speed, & Growth v6.4
12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 14.4.2 All 500 releases
jetpack / sync / class.jetpack-sync-module-callables.php
class.jetpack-sync-module-callables.php
265 lines 8.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 require_once dirname( __FILE__ ) . '/class.jetpack-sync-functions.php';
4
5 class Jetpack_Sync_Module_Callables extends Jetpack_Sync_Module {
6 const CALLABLES_CHECKSUM_OPTION_NAME = 'jetpack_callables_sync_checksum';
7 const CALLABLES_AWAIT_TRANSIENT_NAME = 'jetpack_sync_callables_await';
8
9 private $callable_whitelist;
10
11 public function name() {
12 return 'functions';
13 }
14
15 public function set_defaults() {
16 if ( is_multisite() ) {
17 $this->callable_whitelist = array_merge( Jetpack_Sync_Defaults::get_callable_whitelist(), Jetpack_Sync_Defaults::get_multisite_callable_whitelist() );
18 } else {
19 $this->callable_whitelist = Jetpack_Sync_Defaults::get_callable_whitelist();
20 }
21 }
22
23 public function init_listeners( $callable ) {
24 add_action( 'jetpack_sync_callable', $callable, 10, 2 );
25 add_action( 'admin_init', array( $this, 'set_plugin_action_links' ), 9999 ); // Should happen very late
26
27 // For some options, we should always send the change right away!
28 $always_send_updates_to_these_options = array(
29 'jetpack_active_modules',
30 'home',
31 'siteurl',
32 'jetpack_sync_error_idc',
33 );
34 foreach( $always_send_updates_to_these_options as $option ) {
35 add_action( "update_option_{$option}", array( $this, 'unlock_sync_callable' ) );
36 }
37
38 // Provide a hook so that hosts can send changes to certain callables right away.
39 // Especially useful when a host uses constants to change home and siteurl.
40 add_action( 'jetpack_sync_unlock_sync_callable', array( $this, 'unlock_sync_callable' ) );
41
42 // get_plugins and wp_version
43 // gets fired when new code gets installed, updates etc.
44 add_action( 'upgrader_process_complete', array( $this, 'unlock_plugin_action_link_and_callables' ) );
45 add_action( 'update_option_active_plugins', array( $this, 'unlock_plugin_action_link_and_callables' ) );
46 }
47
48 public function init_full_sync_listeners( $callable ) {
49 add_action( 'jetpack_full_sync_callables', $callable );
50 }
51
52 public function init_before_send() {
53 add_action( 'jetpack_sync_before_send_queue_sync', array( $this, 'maybe_sync_callables' ) );
54
55 // full sync
56 add_filter( 'jetpack_sync_before_send_jetpack_full_sync_callables', array( $this, 'expand_callables' ) );
57 }
58
59 public function reset_data() {
60 delete_option( self::CALLABLES_CHECKSUM_OPTION_NAME );
61 delete_transient( self::CALLABLES_AWAIT_TRANSIENT_NAME );
62
63 $url_callables = array( 'home_url', 'site_url', 'main_network_site_url' );
64 foreach( $url_callables as $callable ) {
65 delete_option( Jetpack_Sync_Functions::HTTPS_CHECK_OPTION_PREFIX . $callable );
66 }
67 }
68
69 function set_callable_whitelist( $callables ) {
70 $this->callable_whitelist = $callables;
71 }
72
73 function get_callable_whitelist() {
74 return $this->callable_whitelist;
75 }
76
77 public function get_all_callables() {
78 // get_all_callables should run as the master user always.
79 $current_user_id = get_current_user_id();
80 wp_set_current_user( Jetpack_Options::get_option( 'master_user' ) );
81 $callables = array_combine(
82 array_keys( $this->get_callable_whitelist() ),
83 array_map( array( $this, 'get_callable' ), array_values( $this->get_callable_whitelist() ) )
84 );
85 wp_set_current_user( $current_user_id );
86 return $callables;
87 }
88
89 private function get_callable( $callable ) {
90 return call_user_func( $callable );
91 }
92
93 public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) {
94 /**
95 * Tells the client to sync all callables to the server
96 *
97 * @since 4.2.0
98 *
99 * @param boolean Whether to expand callables (should always be true)
100 */
101 do_action( 'jetpack_full_sync_callables', true );
102
103 // The number of actions enqueued, and next module state (true == done)
104 return array( 1, true );
105 }
106
107 public function estimate_full_sync_actions( $config ) {
108 return 1;
109 }
110
111 public function get_full_sync_actions() {
112 return array( 'jetpack_full_sync_callables' );
113 }
114
115 public function unlock_sync_callable() {
116 delete_transient( self::CALLABLES_AWAIT_TRANSIENT_NAME );
117 }
118
119 public function unlock_plugin_action_link_and_callables() {
120 delete_transient( self::CALLABLES_AWAIT_TRANSIENT_NAME );
121 delete_transient( 'jetpack_plugin_api_action_links_refresh' );
122 add_filter( 'jetpack_check_and_send_callables', '__return_true' );
123 }
124
125 public function set_plugin_action_links() {
126 if (
127 ! class_exists( 'DOMDocument' ) ||
128 ! function_exists ( 'libxml_use_internal_errors' ) ||
129 ! function_exists ( 'mb_convert_encoding' )
130 ) {
131 return;
132 }
133
134 $plugins_action_links = array();
135 // Is the transient lock in place?
136 $plugins_lock = get_transient( 'jetpack_plugin_api_action_links_refresh', false );
137 if ( ! empty( $plugins_lock ) ) {
138 return;
139 }
140 $plugins = array_keys( Jetpack_Sync_Functions::get_plugins() );
141 foreach ( $plugins as $plugin_file ) {
142 /**
143 * Plugins often like to unset things but things break if they are not able to.
144 */
145 $action_links = array(
146 'deactivate' => '',
147 'activate' => '',
148 'details' => '',
149 'delete' => '',
150 'edit' => ''
151 );
152 /** This filter is documented in src/wp-admin/includes/class-wp-plugins-list-table.php */
153 $action_links = apply_filters( 'plugin_action_links', $action_links, $plugin_file, null, 'all' );
154 /** This filter is documented in src/wp-admin/includes/class-wp-plugins-list-table.php */
155 $action_links = apply_filters( "plugin_action_links_{$plugin_file}", $action_links, $plugin_file, null, 'all' );
156 $action_links = array_filter( $action_links );
157 $formatted_action_links = null;
158 if ( ! empty( $action_links ) && count( $action_links ) > 0 ) {
159 $dom_doc = new DOMDocument;
160 foreach ( $action_links as $action_link ) {
161 // The @ is not enough to suppress errors when dealing with libxml,
162 // we have to tell it directly how we want to handle errors.
163 libxml_use_internal_errors( true );
164 $dom_doc->loadHTML( mb_convert_encoding( $action_link, 'HTML-ENTITIES', 'UTF-8' ) );
165 libxml_use_internal_errors( false );
166
167 $link_elements = $dom_doc->getElementsByTagName( 'a' );
168 if ( $link_elements->length == 0 ) {
169 continue;
170 }
171
172 $link_element = $link_elements->item( 0 );
173 if ( $link_element->hasAttribute( 'href' ) && $link_element->nodeValue ) {
174 $link_url = trim( $link_element->getAttribute( 'href' ) );
175
176 // Add the full admin path to the url if the plugin did not provide it
177 $link_url_scheme = wp_parse_url( $link_url, PHP_URL_SCHEME );
178 if ( empty( $link_url_scheme ) ) {
179 $link_url = admin_url( $link_url );
180 }
181
182 $formatted_action_links[ $link_element->nodeValue ] = $link_url;
183 }
184 }
185 }
186 if ( $formatted_action_links ) {
187 $plugins_action_links[ $plugin_file ] = $formatted_action_links;
188 }
189 }
190 // Cache things for a long time
191 set_transient( 'jetpack_plugin_api_action_links_refresh', time(), DAY_IN_SECONDS );
192 update_option( 'jetpack_plugin_api_action_links', $plugins_action_links );
193 }
194
195 public function should_send_callable( $callable_checksums, $name, $checksum ) {
196 $idc_override_callables = array(
197 'main_network_site',
198 'home_url',
199 'site_url',
200 );
201 if ( in_array( $name, $idc_override_callables ) && Jetpack_Options::get_option( 'migrate_for_idc' ) ) {
202 return true;
203 }
204
205 return ! $this->still_valid_checksum( $callable_checksums, $name, $checksum );
206 }
207
208 public function maybe_sync_callables() {
209 if ( ! apply_filters( 'jetpack_check_and_send_callables', false ) ) {
210 if ( ! is_admin() || Jetpack_Sync_Settings::is_doing_cron() ) {
211 return;
212 }
213
214 if ( get_transient( self::CALLABLES_AWAIT_TRANSIENT_NAME ) ) {
215 return;
216 }
217 }
218
219 set_transient( self::CALLABLES_AWAIT_TRANSIENT_NAME, microtime( true ), Jetpack_Sync_Defaults::$default_sync_callables_wait_time );
220
221 $callables = $this->get_all_callables();
222
223 if ( empty( $callables ) ) {
224 return;
225 }
226
227 $callable_checksums = (array) Jetpack_Options::get_raw_option( self::CALLABLES_CHECKSUM_OPTION_NAME, array() );
228
229 // only send the callables that have changed
230 foreach ( $callables as $name => $value ) {
231 $checksum = $this->get_check_sum( $value );
232 // explicitly not using Identical comparison as get_option returns a string
233 if ( ! is_null( $value ) && $this->should_send_callable( $callable_checksums, $name, $checksum ) ) {
234 /**
235 * Tells the client to sync a callable (aka function) to the server
236 *
237 * @since 4.2.0
238 *
239 * @param string The name of the callable
240 * @param mixed The value of the callable
241 */
242 do_action( 'jetpack_sync_callable', $name, $value );
243 $callable_checksums[ $name ] = $checksum;
244 } else {
245 $callable_checksums[ $name ] = $checksum;
246 }
247 }
248 Jetpack_Options::update_raw_option( self::CALLABLES_CHECKSUM_OPTION_NAME, $callable_checksums );
249 }
250
251 public function expand_callables( $args ) {
252 if ( $args[0] ) {
253 $callables = $this->get_all_callables();
254 $callables_checksums = array();
255 foreach ( $callables as $name => $value ) {
256 $callables_checksums[ $name ] = $this->get_check_sum( $value );
257 }
258 Jetpack_Options::update_raw_option( self::CALLABLES_CHECKSUM_OPTION_NAME, $callables_checksums );
259 return $callables;
260 }
261
262 return $args;
263 }
264 }
265