class.jetpack-sync-functions.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. <?php
  2. /*
  3. * Utility functions to generate data synced to wpcom
  4. */
  5. class Jetpack_Sync_Functions {
  6. const HTTPS_CHECK_OPTION_PREFIX = 'jetpack_sync_https_history_';
  7. const HTTPS_CHECK_HISTORY = 5;
  8. public static function get_modules() {
  9. require_once( JETPACK__PLUGIN_DIR . 'class.jetpack-admin.php' );
  10. return Jetpack_Admin::init()->get_modules();
  11. }
  12. public static function get_taxonomies() {
  13. global $wp_taxonomies;
  14. $wp_taxonomies_without_callbacks = array();
  15. foreach ( $wp_taxonomies as $taxonomy_name => $taxonomy ) {
  16. $sanitized_taxonomy = self::sanitize_taxonomy( $taxonomy );
  17. if ( ! empty( $sanitized_taxonomy ) ) {
  18. $wp_taxonomies_without_callbacks[ $taxonomy_name ] = $sanitized_taxonomy;
  19. } else {
  20. error_log( 'Jetpack: Encountered a recusive taxonomy:' . $taxonomy_name );
  21. }
  22. }
  23. return $wp_taxonomies_without_callbacks;
  24. }
  25. public static function get_shortcodes() {
  26. global $shortcode_tags;
  27. return array_keys( $shortcode_tags );
  28. }
  29. /**
  30. * Removes any callback data since we will not be able to process it on our side anyways.
  31. */
  32. public static function sanitize_taxonomy( $taxonomy ) {
  33. // Lets clone the taxonomy object instead of modifing the global one.
  34. $cloned_taxonomy = json_decode( wp_json_encode( $taxonomy ) );
  35. // recursive taxonomies are no fun.
  36. if ( is_null( $cloned_taxonomy ) ) {
  37. return null;
  38. }
  39. // Remove any meta_box_cb if they are not the default wp ones.
  40. if ( isset( $cloned_taxonomy->meta_box_cb ) &&
  41. ! in_array( $cloned_taxonomy->meta_box_cb, array( 'post_tags_meta_box', 'post_categories_meta_box' ) ) ) {
  42. $cloned_taxonomy->meta_box_cb = null;
  43. }
  44. // Remove update call back
  45. if ( isset( $cloned_taxonomy->update_count_callback ) &&
  46. ! is_null( $cloned_taxonomy->update_count_callback ) ) {
  47. $cloned_taxonomy->update_count_callback = null;
  48. }
  49. // Remove rest_controller_class if it something other then the default.
  50. if ( isset( $cloned_taxonomy->rest_controller_class ) &&
  51. 'WP_REST_Terms_Controller' !== $cloned_taxonomy->rest_controller_class ) {
  52. $cloned_taxonomy->rest_controller_class = null;
  53. }
  54. return $cloned_taxonomy;
  55. }
  56. public static function get_post_types() {
  57. global $wp_post_types;
  58. $post_types_without_callbacks = array();
  59. foreach ( $wp_post_types as $post_type_name => $post_type ) {
  60. $sanitized_post_type = self::sanitize_post_type( $post_type );
  61. if ( ! empty( $sanitized_post_type ) ) {
  62. $post_types_without_callbacks[ $post_type_name ] = $sanitized_post_type;
  63. } else {
  64. error_log( 'Jetpack: Encountered a recusive post_type:' . $post_type_name );
  65. }
  66. }
  67. return $post_types_without_callbacks;
  68. }
  69. public static function sanitize_post_type( $post_type ) {
  70. // Lets clone the post type object instead of modifing the global one.
  71. $sanitized_post_type = array();
  72. foreach ( Jetpack_Sync_Defaults::$default_post_type_attributes as $attribute_key => $default_value ) {
  73. if ( isset( $post_type->{ $attribute_key } ) ) {
  74. $sanitized_post_type[ $attribute_key ] = $post_type->{ $attribute_key };
  75. }
  76. }
  77. return (object) $sanitized_post_type;
  78. }
  79. public static function expand_synced_post_type( $sanitized_post_type, $post_type ) {
  80. $post_type = sanitize_key( $post_type );
  81. $post_type_object = new WP_Post_Type( $post_type, $sanitized_post_type );
  82. $post_type_object->add_supports();
  83. $post_type_object->add_rewrite_rules();
  84. $post_type_object->add_hooks();
  85. $post_type_object->register_taxonomies();
  86. return (object) $post_type_object;
  87. }
  88. public static function get_post_type_features() {
  89. global $_wp_post_type_features;
  90. return $_wp_post_type_features;
  91. }
  92. public static function get_hosting_provider() {
  93. if ( defined( 'GD_SYSTEM_PLUGIN_DIR' ) || class_exists( '\\WPaaS\\Plugin' ) ) {
  94. return 'gd-managed-wp';
  95. }
  96. if ( defined( 'MM_BASE_DIR' ) ) {
  97. return 'bh';
  98. }
  99. if ( defined( 'IS_PRESSABLE' ) ) {
  100. return 'pressable';
  101. }
  102. if ( function_exists( 'is_wpe' ) || function_exists( 'is_wpe_snapshot' ) ) {
  103. return 'wpe';
  104. }
  105. if ( defined( 'VIP_GO_ENV' ) && false !== VIP_GO_ENV ) {
  106. return 'vip-go';
  107. }
  108. return 'unknown';
  109. }
  110. public static function rest_api_allowed_post_types() {
  111. /** This filter is already documented in class.json-api-endpoints.php */
  112. return apply_filters( 'rest_api_allowed_post_types', array( 'post', 'page', 'revision' ) );
  113. }
  114. public static function rest_api_allowed_public_metadata() {
  115. /** This filter is documented in json-endpoints/class.wpcom-json-api-post-endpoint.php */
  116. return apply_filters( 'rest_api_allowed_public_metadata', array() );
  117. }
  118. /**
  119. * Finds out if a site is using a version control system.
  120. * @return bool
  121. **/
  122. public static function is_version_controlled() {
  123. if ( ! class_exists( 'WP_Automatic_Updater' ) ) {
  124. require_once( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' );
  125. }
  126. $updater = new WP_Automatic_Updater();
  127. return (bool) strval( $updater->is_vcs_checkout( $context = ABSPATH ) );
  128. }
  129. /**
  130. * Returns true if the site has file write access false otherwise.
  131. * @return bool
  132. **/
  133. public static function file_system_write_access() {
  134. if ( ! function_exists( 'get_filesystem_method' ) ) {
  135. require_once( ABSPATH . 'wp-admin/includes/file.php' );
  136. }
  137. require_once( ABSPATH . 'wp-admin/includes/template.php' );
  138. $filesystem_method = get_filesystem_method();
  139. if ( 'direct' === $filesystem_method ) {
  140. return true;
  141. }
  142. ob_start();
  143. if ( ! function_exists( 'request_filesystem_credentials' ) ) {
  144. require_once( ABSPATH . 'wp-admin/includes/file.php' );
  145. }
  146. $filesystem_credentials_are_stored = request_filesystem_credentials( self_admin_url() );
  147. ob_end_clean();
  148. if ( $filesystem_credentials_are_stored ) {
  149. return true;
  150. }
  151. return false;
  152. }
  153. /**
  154. * Helper function that is used when getting home or siteurl values. Decides
  155. * whether to get the raw or filtered value.
  156. *
  157. * @return string
  158. */
  159. public static function get_raw_or_filtered_url( $url_type ) {
  160. $url_function = ( 'home' == $url_type )
  161. ? 'home_url'
  162. : 'site_url';
  163. if (
  164. ! Jetpack_Constants::is_defined( 'JETPACK_SYNC_USE_RAW_URL' ) ||
  165. Jetpack_Constants::get_constant( 'JETPACK_SYNC_USE_RAW_URL' )
  166. ) {
  167. $scheme = is_ssl() ? 'https' : 'http';
  168. $url = self::get_raw_url( $url_type );
  169. $url = set_url_scheme( $url, $scheme );
  170. } else {
  171. $url = self::normalize_www_in_url( $url_type, $url_function );
  172. }
  173. return self::get_protocol_normalized_url( $url_function, $url );
  174. }
  175. public static function home_url() {
  176. $url = self::get_raw_or_filtered_url( 'home' );
  177. /**
  178. * Allows overriding of the home_url value that is synced back to WordPress.com.
  179. *
  180. * @since 5.2
  181. *
  182. * @param string $home_url
  183. */
  184. return esc_url_raw( apply_filters( 'jetpack_sync_home_url', $url ) );
  185. }
  186. public static function site_url() {
  187. $url = self::get_raw_or_filtered_url( 'siteurl' );
  188. /**
  189. * Allows overriding of the site_url value that is synced back to WordPress.com.
  190. *
  191. * @since 5.2
  192. *
  193. * @param string $site_url
  194. */
  195. return esc_url_raw( apply_filters( 'jetpack_sync_site_url', $url ) );
  196. }
  197. public static function main_network_site_url() {
  198. return self::get_protocol_normalized_url( 'main_network_site_url', network_site_url() );
  199. }
  200. public static function get_protocol_normalized_url( $callable, $new_value ) {
  201. $option_key = self::HTTPS_CHECK_OPTION_PREFIX . $callable;
  202. $parsed_url = wp_parse_url( $new_value );
  203. if ( ! $parsed_url ) {
  204. return $new_value;
  205. }
  206. if ( array_key_exists ( 'scheme' , $parsed_url ) ) {
  207. $scheme = $parsed_url['scheme'];
  208. } else {
  209. $scheme = '';
  210. }
  211. $scheme_history = get_option( $option_key, array() );
  212. $scheme_history[] = $scheme;
  213. // Limit length to self::HTTPS_CHECK_HISTORY
  214. $scheme_history = array_slice( $scheme_history, ( self::HTTPS_CHECK_HISTORY * -1 ) );
  215. update_option( $option_key, $scheme_history );
  216. $forced_scheme = in_array( 'https', $scheme_history ) ? 'https' : 'http';
  217. return set_url_scheme( $new_value, $forced_scheme );
  218. }
  219. public static function get_raw_url( $option_name ) {
  220. $value = null;
  221. $constant = ( 'home' == $option_name )
  222. ? 'WP_HOME'
  223. : 'WP_SITEURL';
  224. // Since we disregard the constant for multisites in ms-default-filters.php,
  225. // let's also use the db value if this is a multisite.
  226. if ( ! is_multisite() && Jetpack_Constants::is_defined( $constant ) ) {
  227. $value = Jetpack_Constants::get_constant( $constant );
  228. } else {
  229. // Let's get the option from the database so that we can bypass filters. This will help
  230. // ensure that we get more uniform values.
  231. $value = Jetpack_Options::get_raw_option( $option_name );
  232. }
  233. return $value;
  234. }
  235. public static function normalize_www_in_url( $option, $url_function ) {
  236. $url = wp_parse_url( call_user_func( $url_function ) );
  237. $option_url = wp_parse_url( get_option( $option ) );
  238. if ( ! $option_url || ! $url ) {
  239. return $url;
  240. }
  241. if ( $url[ 'host' ] === "www.{$option_url[ 'host' ]}" ) {
  242. // remove www if not present in option URL
  243. $url[ 'host' ] = $option_url[ 'host' ];
  244. }
  245. if ( $option_url[ 'host' ] === "www.{$url[ 'host' ]}" ) {
  246. // add www if present in option URL
  247. $url[ 'host' ] = $option_url[ 'host' ];
  248. }
  249. $normalized_url = "{$url['scheme']}://{$url['host']}";
  250. if ( isset( $url['path'] ) ) {
  251. $normalized_url .= "{$url['path']}";
  252. }
  253. if ( isset( $url['query'] ) ) {
  254. $normalized_url .= "?{$url['query']}";
  255. }
  256. return $normalized_url;
  257. }
  258. public static function get_plugins() {
  259. if ( ! function_exists( 'get_plugins' ) ) {
  260. require_once ABSPATH . 'wp-admin/includes/plugin.php';
  261. }
  262. /** This filter is documented in wp-admin/includes/class-wp-plugins-list-table.php */
  263. return apply_filters( 'all_plugins', get_plugins() );
  264. }
  265. /**
  266. * Get custom action link tags that the plugin is using
  267. * Ref: https://codex.wordpress.org/Plugin_API/Filter_Reference/plugin_action_links_(plugin_file_name)
  268. * @return array of plugin action links (key: link name value: url)
  269. */
  270. public static function get_plugins_action_links( $plugin_file_singular = null ) {
  271. // Some sites may have DOM disabled in PHP fail early
  272. if ( ! class_exists( 'DOMDocument' ) ) {
  273. return array();
  274. }
  275. $plugins_action_links = get_option( 'jetpack_plugin_api_action_links', array() );
  276. if ( ! empty( $plugins_action_links ) ) {
  277. if ( is_null( $plugin_file_singular ) ) {
  278. return $plugins_action_links;
  279. }
  280. return ( isset( $plugins_action_links[ $plugin_file_singular ] ) ? $plugins_action_links[ $plugin_file_singular ] : null );
  281. }
  282. return array();
  283. }
  284. public static function wp_version() {
  285. global $wp_version;
  286. return $wp_version;
  287. }
  288. public static function site_icon_url( $size = 512 ) {
  289. if ( ! function_exists( 'get_site_icon_url' ) || ! has_site_icon() ) {
  290. return get_option( 'jetpack_site_icon_url' );
  291. }
  292. return get_site_icon_url( $size );
  293. }
  294. public static function roles() {
  295. $wp_roles = wp_roles();
  296. return $wp_roles->roles;
  297. }
  298. /**
  299. * Determine time zone from WordPress' options "timezone_string"
  300. * and "gmt_offset".
  301. *
  302. * 1. Check if `timezone_string` is set and return it.
  303. * 2. Check if `gmt_offset` is set, formats UTC-offset from it and return it.
  304. * 3. Default to "UTC+0" if nothing is set.
  305. *
  306. * @return string
  307. */
  308. public static function get_timezone() {
  309. $timezone_string = get_option( 'timezone_string' );
  310. if ( ! empty( $timezone_string ) ) {
  311. return str_replace( '_', ' ', $timezone_string );
  312. }
  313. $gmt_offset = get_option( 'gmt_offset', 0 );
  314. $formatted_gmt_offset = sprintf( '%+g', floatval( $gmt_offset ) );
  315. $formatted_gmt_offset = str_replace(
  316. array( '.25', '.5', '.75' ),
  317. array( ':15', ':30', ':45' ),
  318. (string) $formatted_gmt_offset
  319. );
  320. /* translators: %s is UTC offset, e.g. "+1" */
  321. return sprintf( __( 'UTC%s', 'jetpack' ), $formatted_gmt_offset );
  322. }
  323. }